/* THIS FILE WAS GENERATED BY THE DATALIGHT RELIANCE EDGE CONFIGURATION\r
UTILITY. DO NOT MODIFY.\r
+\r
+ Generated by configuration utility version 2.0\r
*/\r
/** @file\r
*/\r
\r
const VOLCONF gaRedVolConf[REDCONF_VOLUME_COUNT] =\r
{\r
- { 512U, 65536U, false, 256U, 0, "" }\r
+ { 512U, 65536U, false, 256U, 0U, "" }\r
};\r
/* THIS FILE WAS GENERATED BY THE DATALIGHT RELIANCE EDGE CONFIGURATION\r
UTILITY. DO NOT MODIFY.\r
+\r
+ Generated by configuration utility version 2.0\r
*/\r
/** @file\r
*/\r
\r
#define REDCONF_IMAP_EXTERNAL 1\r
\r
+#define REDCONF_DISCARDS 0\r
+\r
#define REDCONF_IMAGE_BUILDER 0\r
\r
#define REDCONF_CHECKER 0\r
\r
+#define RED_CONFIG_UTILITY_VERSION 0x2000000U\r
+\r
+#define RED_CONFIG_MINCOMPAT_VER 0x1000200U\r
+\r
#endif\r
\r
/* Create a string that includes the file name, the file size and the\r
attributes string. */\r
- sprintf( pcBuffer, "%s [%s] [size=%d]", pxDirent->d_name, pcAttrib, pxDirent->d_stat.st_size );\r
+ sprintf( pcBuffer, "%s [%s] [size=%lld]", pxDirent->d_name, pcAttrib, pxDirent->d_stat.st_size );\r
}\r
/* If Win32 port */\r
#ifdef WIN32\r
\r
- #undef _WIN32_WINNT\r
- #define _WIN32_WINNT 0x0600\r
-\r
/* Standard includes. */\r
#include <stdio.h>\r
#include <windows.h>\r
#define HWTC_COUNT (*((uint32_t*)0xE000E018))\r
#define HWTC_PERIOD ((*(uint32_t*)0xE000E014) + 1)\r
#define HWTC_DIVISOR 2\r
- \r
+\r
#define IRQ_PRIORITY_ORDER 0 // lower IRQ priority values are more significant\r
- \r
+\r
#elif (SELECTED_PORT == PORT_Renesas_RX600)\r
\r
#include "iodefine.h"\r
volatile int recorder_busy = 0;\r
\r
/* Gives the last error message of the recorder. NULL if no error message. */\r
-char* traceErrorMessage = NULL;\r
+const char* traceErrorMessage = NULL;\r
\r
void* xTraceNextFreeEventBufferSlot(void)\r
{\r
\r
if (traceErrorMessage == NULL)\r
{\r
- traceErrorMessage = (char*)msg;\r
+ traceErrorMessage = msg;\r
(void)strncpy(RecorderDataPtr->systemInfo, traceErrorMessage, 80);\r
RecorderDataPtr->internalErrorOccured = 1;\r
}\r
-Reliance Edge Credits\r
-=====================\r
-\r
-This is a list (or partial list) of people who have made non-trivial or\r
-noteworthy contributions to the Reliance Edge project. It is sorted by name.\r
-Entries are formatted as below:\r
-\r
-Real Name (githubaccount)\r
-Short description of how Real Name contributed to Reliance Edge.\r
-\r
-The real name may be withheld by request and the GitHub account name might be\r
-missing if the contributor does not use GitHub.\r
-\r
-Credits\r
--------\r
-\r
-None yet! ;)\r
-\r
+Reliance Edge Credits
+=====================
+
+This is a list (or partial list) of people who have made non-trivial or
+noteworthy contributions to the Reliance Edge project. It is sorted by name.
+Entries are formatted as below:
+
+Real Name (githubaccount)
+Short description of how Real Name contributed to Reliance Edge.
+
+The real name may be withheld by request and the GitHub account name might be
+missing if the contributor does not use GitHub.
+
+Credits
+-------
+
+Jean-Christophe Dubois (jcdubois)
+Created a user-space Linux port of Reliance Edge, ported the open-source host
+tools (including the formatter and image builder) to Linux, and created a FUSE
+(File System in User Space) implementation for Reliance Edge.
+
- GNU GENERAL PUBLIC LICENSE\r
- Version 2, June 1991\r
-\r
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.,\r
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\r
- Everyone is permitted to copy and distribute verbatim copies\r
- of this license document, but changing it is not allowed.\r
-\r
- Preamble\r
-\r
- The licenses for most software are designed to take away your\r
-freedom to share and change it. By contrast, the GNU General Public\r
-License is intended to guarantee your freedom to share and change free\r
-software--to make sure the software is free for all its users. This\r
-General Public License applies to most of the Free Software\r
-Foundation's software and to any other program whose authors commit to\r
-using it. (Some other Free Software Foundation software is covered by\r
-the GNU Lesser General Public License instead.) You can apply it to\r
-your programs, too.\r
-\r
- When we speak of free software, we are referring to freedom, not\r
-price. Our General Public Licenses are designed to make sure that you\r
-have the freedom to distribute copies of free software (and charge for\r
-this service if you wish), that you receive source code or can get it\r
-if you want it, that you can change the software or use pieces of it\r
-in new free programs; and that you know you can do these things.\r
-\r
- To protect your rights, we need to make restrictions that forbid\r
-anyone to deny you these rights or to ask you to surrender the rights.\r
-These restrictions translate to certain responsibilities for you if you\r
-distribute copies of the software, or if you modify it.\r
-\r
- For example, if you distribute copies of such a program, whether\r
-gratis or for a fee, you must give the recipients all the rights that\r
-you have. You must make sure that they, too, receive or can get the\r
-source code. And you must show them these terms so they know their\r
-rights.\r
-\r
- We protect your rights with two steps: (1) copyright the software, and\r
-(2) offer you this license which gives you legal permission to copy,\r
-distribute and/or modify the software.\r
-\r
- Also, for each author's protection and ours, we want to make certain\r
-that everyone understands that there is no warranty for this free\r
-software. If the software is modified by someone else and passed on, we\r
-want its recipients to know that what they have is not the original, so\r
-that any problems introduced by others will not reflect on the original\r
-authors' reputations.\r
-\r
- Finally, any free program is threatened constantly by software\r
-patents. We wish to avoid the danger that redistributors of a free\r
-program will individually obtain patent licenses, in effect making the\r
-program proprietary. To prevent this, we have made it clear that any\r
-patent must be licensed for everyone's free use or not licensed at all.\r
-\r
- The precise terms and conditions for copying, distribution and\r
-modification follow.\r
-\r
- GNU GENERAL PUBLIC LICENSE\r
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\r
-\r
- 0. This License applies to any program or other work which contains\r
-a notice placed by the copyright holder saying it may be distributed\r
-under the terms of this General Public License. The "Program", below,\r
-refers to any such program or work, and a "work based on the Program"\r
-means either the Program or any derivative work under copyright law:\r
-that is to say, a work containing the Program or a portion of it,\r
-either verbatim or with modifications and/or translated into another\r
-language. (Hereinafter, translation is included without limitation in\r
-the term "modification".) Each licensee is addressed as "you".\r
-\r
-Activities other than copying, distribution and modification are not\r
-covered by this License; they are outside its scope. The act of\r
-running the Program is not restricted, and the output from the Program\r
-is covered only if its contents constitute a work based on the\r
-Program (independent of having been made by running the Program).\r
-Whether that is true depends on what the Program does.\r
-\r
- 1. You may copy and distribute verbatim copies of the Program's\r
-source code as you receive it, in any medium, provided that you\r
-conspicuously and appropriately publish on each copy an appropriate\r
-copyright notice and disclaimer of warranty; keep intact all the\r
-notices that refer to this License and to the absence of any warranty;\r
-and give any other recipients of the Program a copy of this License\r
-along with the Program.\r
-\r
-You may charge a fee for the physical act of transferring a copy, and\r
-you may at your option offer warranty protection in exchange for a fee.\r
-\r
- 2. You may modify your copy or copies of the Program or any portion\r
-of it, thus forming a work based on the Program, and copy and\r
-distribute such modifications or work under the terms of Section 1\r
-above, provided that you also meet all of these conditions:\r
-\r
- a) You must cause the modified files to carry prominent notices\r
- stating that you changed the files and the date of any change.\r
-\r
- b) You must cause any work that you distribute or publish, that in\r
- whole or in part contains or is derived from the Program or any\r
- part thereof, to be licensed as a whole at no charge to all third\r
- parties under the terms of this License.\r
-\r
- c) If the modified program normally reads commands interactively\r
- when run, you must cause it, when started running for such\r
- interactive use in the most ordinary way, to print or display an\r
- announcement including an appropriate copyright notice and a\r
- notice that there is no warranty (or else, saying that you provide\r
- a warranty) and that users may redistribute the program under\r
- these conditions, and telling the user how to view a copy of this\r
- License. (Exception: if the Program itself is interactive but\r
- does not normally print such an announcement, your work based on\r
- the Program is not required to print an announcement.)\r
-\r
-These requirements apply to the modified work as a whole. If\r
-identifiable sections of that work are not derived from the Program,\r
-and can be reasonably considered independent and separate works in\r
-themselves, then this License, and its terms, do not apply to those\r
-sections when you distribute them as separate works. But when you\r
-distribute the same sections as part of a whole which is a work based\r
-on the Program, the distribution of the whole must be on the terms of\r
-this License, whose permissions for other licensees extend to the\r
-entire whole, and thus to each and every part regardless of who wrote it.\r
-\r
-Thus, it is not the intent of this section to claim rights or contest\r
-your rights to work written entirely by you; rather, the intent is to\r
-exercise the right to control the distribution of derivative or\r
-collective works based on the Program.\r
-\r
-In addition, mere aggregation of another work not based on the Program\r
-with the Program (or with a work based on the Program) on a volume of\r
-a storage or distribution medium does not bring the other work under\r
-the scope of this License.\r
-\r
- 3. You may copy and distribute the Program (or a work based on it,\r
-under Section 2) in object code or executable form under the terms of\r
-Sections 1 and 2 above provided that you also do one of the following:\r
-\r
- a) Accompany it with the complete corresponding machine-readable\r
- source code, which must be distributed under the terms of Sections\r
- 1 and 2 above on a medium customarily used for software interchange; or,\r
-\r
- b) Accompany it with a written offer, valid for at least three\r
- years, to give any third party, for a charge no more than your\r
- cost of physically performing source distribution, a complete\r
- machine-readable copy of the corresponding source code, to be\r
- distributed under the terms of Sections 1 and 2 above on a medium\r
- customarily used for software interchange; or,\r
-\r
- c) Accompany it with the information you received as to the offer\r
- to distribute corresponding source code. (This alternative is\r
- allowed only for noncommercial distribution and only if you\r
- received the program in object code or executable form with such\r
- an offer, in accord with Subsection b above.)\r
-\r
-The source code for a work means the preferred form of the work for\r
-making modifications to it. For an executable work, complete source\r
-code means all the source code for all modules it contains, plus any\r
-associated interface definition files, plus the scripts used to\r
-control compilation and installation of the executable. However, as a\r
-special exception, the source code distributed need not include\r
-anything that is normally distributed (in either source or binary\r
-form) with the major components (compiler, kernel, and so on) of the\r
-operating system on which the executable runs, unless that component\r
-itself accompanies the executable.\r
-\r
-If distribution of executable or object code is made by offering\r
-access to copy from a designated place, then offering equivalent\r
-access to copy the source code from the same place counts as\r
-distribution of the source code, even though third parties are not\r
-compelled to copy the source along with the object code.\r
-\r
- 4. You may not copy, modify, sublicense, or distribute the Program\r
-except as expressly provided under this License. Any attempt\r
-otherwise to copy, modify, sublicense or distribute the Program is\r
-void, and will automatically terminate your rights under this License.\r
-However, parties who have received copies, or rights, from you under\r
-this License will not have their licenses terminated so long as such\r
-parties remain in full compliance.\r
-\r
- 5. You are not required to accept this License, since you have not\r
-signed it. However, nothing else grants you permission to modify or\r
-distribute the Program or its derivative works. These actions are\r
-prohibited by law if you do not accept this License. Therefore, by\r
-modifying or distributing the Program (or any work based on the\r
-Program), you indicate your acceptance of this License to do so, and\r
-all its terms and conditions for copying, distributing or modifying\r
-the Program or works based on it.\r
-\r
- 6. Each time you redistribute the Program (or any work based on the\r
-Program), the recipient automatically receives a license from the\r
-original licensor to copy, distribute or modify the Program subject to\r
-these terms and conditions. You may not impose any further\r
-restrictions on the recipients' exercise of the rights granted herein.\r
-You are not responsible for enforcing compliance by third parties to\r
-this License.\r
-\r
- 7. If, as a consequence of a court judgment or allegation of patent\r
-infringement or for any other reason (not limited to patent issues),\r
-conditions are imposed on you (whether by court order, agreement or\r
-otherwise) that contradict the conditions of this License, they do not\r
-excuse you from the conditions of this License. If you cannot\r
-distribute so as to satisfy simultaneously your obligations under this\r
-License and any other pertinent obligations, then as a consequence you\r
-may not distribute the Program at all. For example, if a patent\r
-license would not permit royalty-free redistribution of the Program by\r
-all those who receive copies directly or indirectly through you, then\r
-the only way you could satisfy both it and this License would be to\r
-refrain entirely from distribution of the Program.\r
-\r
-If any portion of this section is held invalid or unenforceable under\r
-any particular circumstance, the balance of the section is intended to\r
-apply and the section as a whole is intended to apply in other\r
-circumstances.\r
-\r
-It is not the purpose of this section to induce you to infringe any\r
-patents or other property right claims or to contest validity of any\r
-such claims; this section has the sole purpose of protecting the\r
-integrity of the free software distribution system, which is\r
-implemented by public license practices. Many people have made\r
-generous contributions to the wide range of software distributed\r
-through that system in reliance on consistent application of that\r
-system; it is up to the author/donor to decide if he or she is willing\r
-to distribute software through any other system and a licensee cannot\r
-impose that choice.\r
-\r
-This section is intended to make thoroughly clear what is believed to\r
-be a consequence of the rest of this License.\r
-\r
- 8. If the distribution and/or use of the Program is restricted in\r
-certain countries either by patents or by copyrighted interfaces, the\r
-original copyright holder who places the Program under this License\r
-may add an explicit geographical distribution limitation excluding\r
-those countries, so that distribution is permitted only in or among\r
-countries not thus excluded. In such case, this License incorporates\r
-the limitation as if written in the body of this License.\r
-\r
- 9. The Free Software Foundation may publish revised and/or new versions\r
-of the General Public License from time to time. Such new versions will\r
-be similar in spirit to the present version, but may differ in detail to\r
-address new problems or concerns.\r
-\r
-Each version is given a distinguishing version number. If the Program\r
-specifies a version number of this License which applies to it and "any\r
-later version", you have the option of following the terms and conditions\r
-either of that version or of any later version published by the Free\r
-Software Foundation. If the Program does not specify a version number of\r
-this License, you may choose any version ever published by the Free Software\r
-Foundation.\r
-\r
- 10. If you wish to incorporate parts of the Program into other free\r
-programs whose distribution conditions are different, write to the author\r
-to ask for permission. For software which is copyrighted by the Free\r
-Software Foundation, write to the Free Software Foundation; we sometimes\r
-make exceptions for this. Our decision will be guided by the two goals\r
-of preserving the free status of all derivatives of our free software and\r
-of promoting the sharing and reuse of software generally.\r
-\r
- NO WARRANTY\r
-\r
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\r
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN\r
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\r
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\r
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\r
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS\r
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE\r
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\r
-REPAIR OR CORRECTION.\r
-\r
- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\r
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\r
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\r
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\r
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\r
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\r
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\r
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\r
-POSSIBILITY OF SUCH DAMAGES.\r
-\r
- END OF TERMS AND CONDITIONS\r
-\r
- How to Apply These Terms to Your New Programs\r
-\r
- If you develop a new program, and you want it to be of the greatest\r
-possible use to the public, the best way to achieve this is to make it\r
-free software which everyone can redistribute and change under these terms.\r
-\r
- To do so, attach the following notices to the program. It is safest\r
-to attach them to the start of each source file to most effectively\r
-convey the exclusion of warranty; and each file should have at least\r
-the "copyright" line and a pointer to where the full notice is found.\r
-\r
- <one line to give the program's name and a brief idea of what it does.>\r
- Copyright (C) <year> <name of author>\r
-\r
- This program is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; either version 2 of the License, or\r
- (at your option) any later version.\r
-\r
- This program is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License along\r
- with this program; if not, write to the Free Software Foundation, Inc.,\r
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
-\r
-Also add information on how to contact you by electronic and paper mail.\r
-\r
-If the program is interactive, make it output a short notice like this\r
-when it starts in an interactive mode:\r
-\r
- Gnomovision version 69, Copyright (C) year name of author\r
- Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\r
- This is free software, and you are welcome to redistribute it\r
- under certain conditions; type `show c' for details.\r
-\r
-The hypothetical commands `show w' and `show c' should show the appropriate\r
-parts of the General Public License. Of course, the commands you use may\r
-be called something other than `show w' and `show c'; they could even be\r
-mouse-clicks or menu items--whatever suits your program.\r
-\r
-You should also get your employer (if you work as a programmer) or your\r
-school, if any, to sign a "copyright disclaimer" for the program, if\r
-necessary. Here is a sample; alter the names:\r
-\r
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program\r
- `Gnomovision' (which makes passes at compilers) written by James Hacker.\r
-\r
- <signature of Ty Coon>, 1 April 1989\r
- Ty Coon, President of Vice\r
-\r
-This General Public License does not permit incorporating your program into\r
-proprietary programs. If your program is a subroutine library, you may\r
-consider it more useful to permit linking proprietary applications with the\r
-library. If this is what you want to do, use the GNU Lesser General\r
-Public License instead of this License.\r
-\r
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, 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 Lesser 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 Street, 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.
+
+ <signature of Ty Coon>, 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 Lesser General
+Public License instead of this License.
+
-# Reliance Edge\r
-\r
-Reliance Edge is a small, portable, highly reliable power-fail safe file system\r
-for resource-constrained embedded systems like microcontrollers. It is written\r
-in C and provides a familiar POSIX-like file system API, making it easy to use\r
-in your application; or an alternate minimalist API if your application has\r
-simple storage needs. Reliance Edge is highly configurable and can be tuned to\r
-the precise needs of your application.\r
-\r
-## Documentation\r
-\r
-The complete documentation for Reliance Edge is distributed separately. It\r
-includes an API reference and detailed discussions of various aspects of using\r
-Reliance Edge, including porting, building, configuring, and testing. This\r
-complete documentation, called the _Developer's Guide_, can be obtained for free\r
-from here:\r
-\r
-<http://www.datalight.com/reliance-edge>\r
-\r
-In addition this README, see [doc/release_notes.md](doc/release_notes.md) for a\r
-list of updates to Reliance Edge and a list of known issues. There is also a\r
-quick-start guide in the doc/ directory that describes step-by-step how to\r
-compile and run Reliance Edge in a simulated Windows environment.\r
-\r
-## Why Use Reliance Edge?\r
-\r
-Reliance Edge is ideal for small embedded systems with data storage\r
-requirements, especially if there is a chance of sudden power loss or other\r
-system failures. Compared to "raw" disk access, using a file system like\r
-Reliance Edge removes the burden of tracking which sectors belong to which\r
-objects, and allows data to be updated more reliably. Compared to the FAT file\r
-system, using Reliance Edge eliminates the possibility that file system data\r
-will be left in an inconsistent state, corrupting the disk; Reliance Edge does\r
-not need a fsck/CHKDSK utility. Compared to journaling file systems, Reliance\r
-Edge has less overhead and results in less storage media wear for longer device\r
-lifetimes.\r
-\r
-Reliance Edge uses a unique transactional model that not only prevents file\r
-system corruption but also allows a set of changes to be made in an atomic "all\r
-or nothing" fashion. This is very useful for applications that make sets of\r
-interrelated changes. By using the features of Reliance Edge, a set of changes\r
-can be incorporated into a single atomic transaction, which is committed in its\r
-entirety or not at all even if interrupted by power loss; this means the\r
-application does not need code to recover from partially-finished updates.\r
-\r
-## Hardware\r
-\r
-The typical hardware for Reliance Edge is a 32-bit microcontroller, but other\r
-targets are possible. In its typical configurations, Reliance Edge needs at\r
-least 4 KB to 5 KB of RAM, 11 to 18 KB of code space (on the ROM or NOR flash),\r
-and 500 to 700 bytes of stack.\r
-\r
-Reliance Edge is not designed for high-end embedded systems that run complicated\r
-operating systems like Linux or Windows Embedded Compact. Embedded systems of\r
-that variety are better served by other file systems, like Datalight's\r
-[Reliance Nitro](http://www.datalight.com/products/embedded-file-systems/reliance-nitro).\r
-\r
-## Getting Reliance Edge Working\r
-\r
-Before you can use Reliance Edge, it must be ported and configured. At a\r
-minimum, porting includes filling-in functions so that Reliance Edge can issue\r
-commands to your storage medium; depending on your needs, other functions may\r
-need to be filled in as well. These functions reside in a subdirectory in the\r
-os/ directory; see os/stub/ for a blank set of functions. Configuring includes\r
-creating a project directory (start by copying projects/newproj) and creating\r
-the two configuration files (redconf.h/redconf.c) using the Reliance Edge\r
-Configuration Utility (which can be downloaded from\r
-<http://www.datalight.com/reliance-edge>).\r
-\r
-These topics are covered in much greater detail in the _Developer's Guide_,\r
-linked above.\r
-\r
-## Using Reliance Edge\r
-\r
-Using Reliance Edge is a simple matter of including the primary Reliance Edge\r
-application header in your application (either include/redposix.h or\r
-include/redfse.h) and compiling and linking against Reliance Edge binaries.\r
-The Reliance Edge driver must be initialized before it is used (via the\r
-red\_init() or RedFseInit() functions) and then volumes can be mounted and file\r
-and directory functions invoked. The Reliance Edge API is documented in the\r
-_Developer's Guide_ (linked above) and also via comments in the source code.\r
-\r
-## Licensing\r
-\r
-Reliance Edge is an open-source project licensed under the GNU General Public\r
-License v2 (GPLv2). Businesses and individuals that for commercial or other\r
-reasons cannot comply with the terms of the GPLv2 license may obtain a\r
-commercial license before incorporating Reliance Edge into proprietary software\r
-for distribution in any form. Visit <http://www.datalight.com/reliance-edge>\r
-for more information. The commercial distribution also includes extra tests and\r
-tools not distributed with the GPLv2 version.\r
-\r
-See LICENSE.txt for the full license terms of this distribution of the product.\r
-\r
-## Getting Help\r
-\r
-If you need assistance using Reliance Edge, and you have already consulted the\r
-_Developer's Guide_, contact <RelianceEdgeSupport@datalight.com>.\r
-\r
-In the near future, a community forum or message board will be set up to\r
-facilitate discussion of Reliance Edge and allow users to get help from\r
-Datalight and from each other. In the meantime, please use the email address\r
-given above.\r
-\r
-## Contributing\r
-\r
-Contributions to Reliance Edge are welcome. Our policy is that Datalight must\r
-own the copyright of all code incorporated into Reliance Edge; if contributing a\r
-significant amount of code, you will be asked to file a copyright assignment\r
-agreement. See CONTRIBUTING.txt for further details and contribution\r
-guidelines.\r
-\r
-To report bugs, please create a GitHub issue or contact\r
-<RelianceEdgeSupport@datalight.com>.\r
-\r
+# Reliance Edge
+
+Reliance Edge is a small, portable, highly reliable power-fail safe file system
+for resource-constrained embedded systems like microcontrollers. It is written
+in C and provides a familiar POSIX-like file system API, making it easy to use
+in your application; or an alternate minimalist API if your application has
+simple storage needs. Reliance Edge is highly configurable and can be tuned to
+the precise needs of your application.
+
+## Getting Help
+
+You will find this section later in our readme as well - but we wanted to tell
+you up front: DATALIGHT WANTS TO HEAR FROM YOU! Whether it's just to make
+comments about the product, to suggest new features, or to tell us what you
+don't like - reach out! All comments and inquiries can be directed to
+<RelianceEdgeSupport@datalight.com>.
+
+## Documentation
+
+The complete documentation for Reliance Edge is distributed separately. It
+includes an API reference and detailed discussions of various aspects of using
+Reliance Edge, including porting, building, configuring, and testing. This
+complete documentation, called the _Developer's Guide_, can be obtained for free
+from here:
+
+<http://www.datalight.com/reliance-edge>
+
+In addition this README, see doc/release_notes.md for a list of updates
+to Reliance Edge and a list of known issues. There is also a quick-start
+guide in the doc/ directory that describes step-by-step how to compile
+and run Reliance Edge in a simulated Windows environment.
+
+## Why Use Reliance Edge?
+
+Reliance Edge is ideal for small embedded systems with data storage
+requirements, especially if there is a chance of sudden power loss or other
+system failures. Compared to "raw" disk access, using a file system like
+Reliance Edge removes the burden of tracking which sectors belong to which
+objects, and allows data to be updated more reliably. Compared to the FAT file
+system, using Reliance Edge eliminates the possibility that file system data
+will be left in an inconsistent state, corrupting the disk; Reliance Edge does
+not need a fsck/CHKDSK utility. Compared to journaling file systems, Reliance
+Edge has less overhead and results in less storage media wear for longer device
+lifetimes.
+
+Reliance Edge uses a unique transactional model that not only prevents file
+system corruption but also allows a set of changes to be made in an atomic "all
+or nothing" fashion. This is very useful for applications that make sets of
+interrelated changes. By using the features of Reliance Edge, a set of changes
+can be incorporated into a single atomic transaction, which is committed in its
+entirety or not at all even if interrupted by power loss; this means the
+application does not need code to recover from partially-finished updates.
+
+## Hardware
+
+The typical hardware for Reliance Edge is a 32-bit microcontroller, but other
+targets are possible. In its typical configurations, Reliance Edge needs at
+least 4 KB to 5 KB of RAM, 11 to 18 KB of code space (on the ROM or NOR flash),
+and 500 to 700 bytes of stack.
+
+Reliance Edge is not designed for high-end embedded systems that run complicated
+operating systems like Linux or Windows Embedded Compact. Embedded systems of
+that variety are better served by other file systems, like Datalight's
+[Reliance Nitro](http://www.datalight.com/products/embedded-file-systems/reliance-nitro).
+
+## Getting Reliance Edge Working
+
+Before you can use Reliance Edge, it must be ported and configured. At a
+minimum, porting includes filling-in functions so that Reliance Edge can issue
+commands to your storage medium; depending on your needs, other functions may
+need to be filled in as well. These functions reside in a subdirectory in the
+os/ directory; see os/stub/ for a blank set of functions. Configuring includes
+creating a project directory (start by copying projects/newproj) and creating
+the two configuration files (redconf.h/redconf.c) using the Reliance Edge
+Configuration Utility (which can be downloaded from
+<http://www.datalight.com/reliance-edge>).
+
+These topics are covered in much greater detail in the _Developer's Guide_,
+linked above.
+
+## Using Reliance Edge
+
+Using Reliance Edge is a simple matter of including the primary Reliance Edge
+application header in your application (either include/redposix.h or
+include/redfse.h) and compiling and linking against Reliance Edge binaries.
+The Reliance Edge driver must be initialized before it is used (via the
+red\_init() or RedFseInit() functions) and then volumes can be mounted and file
+and directory functions invoked. The Reliance Edge API is documented in the
+_Developer's Guide_ (linked above) and also via comments in the source code.
+
+## Licensing
+
+Reliance Edge is an open-source project licensed under the GNU General Public
+License v2 (GPLv2). Businesses and individuals that for commercial or other
+reasons cannot comply with the terms of the GPLv2 license may obtain a
+commercial license before incorporating Reliance Edge into proprietary software
+for distribution in any form. Visit <http://www.datalight.com/reliance-edge>
+for more information. The commercial distribution also includes extra tests and
+tools not distributed with the GPLv2 version.
+
+See LICENSE.txt for the full license terms of this distribution of the product.
+
+## Getting Help
+
+If you need assistance using Reliance Edge, and you have already consulted the
+_Developer's Guide_, contact <RelianceEdgeSupport@datalight.com>.
+
+In the near future, a community forum or message board will be set up to
+facilitate discussion of Reliance Edge and allow users to get help from
+Datalight and from each other. In the meantime, please use the email address
+given above.
+
+## Contributing
+
+Contributions to Reliance Edge are welcome. Our policy is that Datalight must
+own the copyright of all code incorporated into Reliance Edge; if contributing a
+significant amount of code, you will be asked to file a copyright assignment
+agreement. See CONTRIBUTING.txt for further details and contribution
+guidelines.
+
+To report bugs, please create a GitHub issue or contact
+<RelianceEdgeSupport@datalight.com>.
+
-\r
-\r
-RELIANCE EDGE\r
-\r
-\r
-Reliance Edge is a small, portable, highly reliable power-fail safe file\r
-system for resource-constrained embedded systems like microcontrollers.\r
-It is written in C and provides a familiar POSIX-like file system API,\r
-making it easy to use in your application; or an alternate minimalist\r
-API if your application has simple storage needs. Reliance Edge is\r
-highly configurable and can be tuned to the precise needs of your\r
-application.\r
-\r
-\r
-Documentation\r
-\r
-The complete documentation for Reliance Edge is distributed separately.\r
-It includes an API reference and detailed discussions of various aspects\r
-of using Reliance Edge, including porting, building, configuring, and\r
-testing. This complete documentation, called the _Developer's Guide_,\r
-can be obtained for free from here:\r
-\r
-http://www.datalight.com/reliance-edge\r
-\r
-In addition this README, see doc/release_notes.md for a list of updates\r
-to Reliance Edge and a list of known issues. There is also a quick-start\r
-guide in the doc/ directory that describes step-by-step how to compile\r
-and run Reliance Edge in a simulated Windows environment.\r
-\r
-\r
-Why Use Reliance Edge?\r
-\r
-Reliance Edge is ideal for small embedded systems with data storage\r
-requirements, especially if there is a chance of sudden power loss or\r
-other system failures. Compared to "raw" disk access, using a file\r
-system like Reliance Edge removes the burden of tracking which sectors\r
-belong to which objects, and allows data to be updated more reliably.\r
-Compared to the FAT file system, using Reliance Edge eliminates the\r
-possibility that file system data will be left in an inconsistent state,\r
-corrupting the disk; Reliance Edge does not need a fsck/CHKDSK utility.\r
-Compared to journaling file systems, Reliance Edge has less overhead and\r
-results in less storage media wear for longer device lifetimes.\r
-\r
-Reliance Edge uses a unique transactional model that not only prevents\r
-file system corruption but also allows a set of changes to be made in an\r
-atomic "all or nothing" fashion. This is very useful for applications\r
-that make sets of interrelated changes. By using the features of\r
-Reliance Edge, a set of changes can be incorporated into a single atomic\r
-transaction, which is committed in its entirety or not at all even if\r
-interrupted by power loss; this means the application does not need code\r
-to recover from partially-finished updates.\r
-\r
-\r
-Hardware\r
-\r
-The typical hardware for Reliance Edge is a 32-bit microcontroller, but\r
-other targets are possible. In its typical configurations, Reliance Edge\r
-needs at least 4 KB to 5 KB of RAM, 11 to 18 KB of code space (on the\r
-ROM or NOR flash), and 500 to 700 bytes of stack.\r
-\r
-Reliance Edge is not designed for high-end embedded systems that run\r
-complicated operating systems like Linux or Windows Embedded Compact.\r
-Embedded systems of that variety are better served by other file\r
-systems, like Datalight's Reliance Nitro.\r
-\r
-\r
-Getting Reliance Edge Working\r
-\r
-Before you can use Reliance Edge, it must be ported and configured. At a\r
-minimum, porting includes filling-in functions so that Reliance Edge can\r
-issue commands to your storage medium; depending on your needs, other\r
-functions may need to be filled in as well. These functions reside in a\r
-subdirectory in the os/ directory; see os/stub/ for a blank set of\r
-functions. Configuring includes creating a project directory (start by\r
-copying projects/newproj) and creating the two configuration files\r
-(redconf.h/redconf.c) using the Reliance Edge Configuration Utility\r
-(which can be downloaded from http://www.datalight.com/reliance-edge).\r
-\r
-These topics are covered in much greater detail in the _Developer's\r
-Guide_, linked above.\r
-\r
-\r
-Using Reliance Edge\r
-\r
-Using Reliance Edge is a simple matter of including the primary Reliance\r
-Edge application header in your application (either include/redposix.h\r
-or include/redfse.h) and compiling and linking against Reliance Edge\r
-binaries. The Reliance Edge driver must be initialized before it is used\r
-(via the red_init() or RedFseInit() functions) and then volumes can be\r
-mounted and file and directory functions invoked. The Reliance Edge API\r
-is documented in the _Developer's Guide_ (linked above) and also via\r
-comments in the source code.\r
-\r
-\r
-Licensing\r
-\r
-Reliance Edge is an open-source project licensed under the GNU General\r
-Public License v2 (GPLv2). Businesses and individuals that for\r
-commercial or other reasons cannot comply with the terms of the GPLv2\r
-license may obtain a commercial license before incorporating Reliance\r
-Edge into proprietary software for distribution in any form. Visit\r
-http://www.datalight.com/reliance-edge for more information. The\r
-commercial distribution also includes extra tests and tools not\r
-distributed with the GPLv2 version.\r
-\r
-See LICENSE.txt for the full license terms of this distribution of the\r
-product.\r
-\r
-\r
-Getting Help\r
-\r
-If you need assistance using Reliance Edge, and you have already\r
-consulted the _Developer's Guide_, contact\r
-RelianceEdgeSupport@datalight.com.\r
-\r
-In the near future, a community forum or message board will be set up to\r
-facilitate discussion of Reliance Edge and allow users to get help from\r
-Datalight and from each other. In the meantime, please use the email\r
-address given above.\r
-\r
-\r
-Contributing\r
-\r
-Contributions to Reliance Edge are welcome. Our policy is that Datalight\r
-must own the copyright of all code incorporated into Reliance Edge; if\r
-contributing a significant amount of code, you will be asked to file a\r
-copyright assignment agreement. See CONTRIBUTING.txt for further details\r
-and contribution guidelines.\r
-\r
-To report bugs, please create a GitHub issue or contact\r
-RelianceEdgeSupport@datalight.com.\r
+
+
+RELIANCE EDGE
+
+
+Reliance Edge is a small, portable, highly reliable power-fail safe file
+system for resource-constrained embedded systems like microcontrollers.
+It is written in C and provides a familiar POSIX-like file system API,
+making it easy to use in your application; or an alternate minimalist
+API if your application has simple storage needs. Reliance Edge is
+highly configurable and can be tuned to the precise needs of your
+application.
+
+
+Getting Help
+
+You will find this section later in our readme as well - but we wanted
+to tell you up front: DATALIGHT WANTS TO HEAR FROM YOU! Whether it's
+just to make comments about the product, to suggest new features, or to
+tell us what you don't like - reach out! All comments and inquiries can
+be directed to RelianceEdgeSupport@datalight.com.
+
+
+Documentation
+
+The complete documentation for Reliance Edge is distributed separately.
+It includes an API reference and detailed discussions of various aspects
+of using Reliance Edge, including porting, building, configuring, and
+testing. This complete documentation, called the _Developer's Guide_,
+can be obtained for free from here:
+
+http://www.datalight.com/reliance-edge
+
+In addition this README, see doc/release_notes.md for a list of updates
+to Reliance Edge and a list of known issues. There is also a quick-start
+guide in the doc/ directory that describes step-by-step how to compile
+and run Reliance Edge in a simulated Windows environment.
+
+
+Why Use Reliance Edge?
+
+Reliance Edge is ideal for small embedded systems with data storage
+requirements, especially if there is a chance of sudden power loss or
+other system failures. Compared to "raw" disk access, using a file
+system like Reliance Edge removes the burden of tracking which sectors
+belong to which objects, and allows data to be updated more reliably.
+Compared to the FAT file system, using Reliance Edge eliminates the
+possibility that file system data will be left in an inconsistent state,
+corrupting the disk; Reliance Edge does not need a fsck/CHKDSK utility.
+Compared to journaling file systems, Reliance Edge has less overhead and
+results in less storage media wear for longer device lifetimes.
+
+Reliance Edge uses a unique transactional model that not only prevents
+file system corruption but also allows a set of changes to be made in an
+atomic "all or nothing" fashion. This is very useful for applications
+that make sets of interrelated changes. By using the features of
+Reliance Edge, a set of changes can be incorporated into a single atomic
+transaction, which is committed in its entirety or not at all even if
+interrupted by power loss; this means the application does not need code
+to recover from partially-finished updates.
+
+
+Hardware
+
+The typical hardware for Reliance Edge is a 32-bit microcontroller, but
+other targets are possible. In its typical configurations, Reliance Edge
+needs at least 4 KB to 5 KB of RAM, 11 to 18 KB of code space (on the
+ROM or NOR flash), and 500 to 700 bytes of stack.
+
+Reliance Edge is not designed for high-end embedded systems that run
+complicated operating systems like Linux or Windows Embedded Compact.
+Embedded systems of that variety are better served by other file
+systems, like Datalight's Reliance Nitro.
+
+
+Getting Reliance Edge Working
+
+Before you can use Reliance Edge, it must be ported and configured. At a
+minimum, porting includes filling-in functions so that Reliance Edge can
+issue commands to your storage medium; depending on your needs, other
+functions may need to be filled in as well. These functions reside in a
+subdirectory in the os/ directory; see os/stub/ for a blank set of
+functions. Configuring includes creating a project directory (start by
+copying projects/newproj) and creating the two configuration files
+(redconf.h/redconf.c) using the Reliance Edge Configuration Utility
+(which can be downloaded from http://www.datalight.com/reliance-edge).
+
+These topics are covered in much greater detail in the _Developer's
+Guide_, linked above.
+
+
+Using Reliance Edge
+
+Using Reliance Edge is a simple matter of including the primary Reliance
+Edge application header in your application (either include/redposix.h
+or include/redfse.h) and compiling and linking against Reliance Edge
+binaries. The Reliance Edge driver must be initialized before it is used
+(via the red_init() or RedFseInit() functions) and then volumes can be
+mounted and file and directory functions invoked. The Reliance Edge API
+is documented in the _Developer's Guide_ (linked above) and also via
+comments in the source code.
+
+
+Licensing
+
+Reliance Edge is an open-source project licensed under the GNU General
+Public License v2 (GPLv2). Businesses and individuals that for
+commercial or other reasons cannot comply with the terms of the GPLv2
+license may obtain a commercial license before incorporating Reliance
+Edge into proprietary software for distribution in any form. Visit
+http://www.datalight.com/reliance-edge for more information. The
+commercial distribution also includes extra tests and tools not
+distributed with the GPLv2 version.
+
+See LICENSE.txt for the full license terms of this distribution of the
+product.
+
+
+Getting Help
+
+If you need assistance using Reliance Edge, and you have already
+consulted the _Developer's Guide_, contact
+RelianceEdgeSupport@datalight.com.
+
+In the near future, a community forum or message board will be set up to
+facilitate discussion of Reliance Edge and allow users to get help from
+Datalight and from each other. In the meantime, please use the email
+address given above.
+
+
+Contributing
+
+Contributions to Reliance Edge are welcome. Our policy is that Datalight
+must own the copyright of all code incorporated into Reliance Edge; if
+contributing a significant amount of code, you will be asked to file a
+copyright assignment agreement. See CONTRIBUTING.txt for further details
+and contribution guidelines.
+
+To report bugs, please create a GitHub issue or contact
+RelianceEdgeSupport@datalight.com.
COREVOLUME * CONST_IF_ONE_VOLUME gpRedCoreVol = &gaCoreVol[0U];\r
METAROOT *gpRedMR = &gaCoreVol[0U].aMR[0U];\r
\r
-CONST_IF_ONE_VOLUME uint8_t gbRedVolNum;\r
+CONST_IF_ONE_VOLUME uint8_t gbRedVolNum = 0;\r
\r
\r
/** @brief Initialize the Reliance Edge file system driver.\r
@param pszSrcName The name of the file or directory to rename.\r
@param ulDstPInode The new parent directory inode number of the file or\r
directory after the rename.\r
- @param pszNewPath The new name of the file or directory after the rename.\r
+ @param pszDstName The new name of the file or directory after the rename.\r
\r
@return A negated ::REDSTATUS code indicating the operation result.\r
\r
@param pszSrcName The name of the file or directory to rename.\r
@param ulDstPInode The new parent directory inode number of the file or\r
directory after the rename.\r
- @param pszNewPath The new name of the file or directory after the rename.\r
+ @param pszDstName The new name of the file or directory after the rename.\r
\r
@return A negated ::REDSTATUS code indicating the operation result.\r
\r
}\r
else if((DirEntryIndexToOffset(ulDeleteIdx) + DIRENT_SIZE) == pPInode->pInodeBuf->ullSize)\r
{\r
- uint32_t ulTruncIdx = ulDeleteIdx;\r
+ /* Start searching one behind the index to be deleted.\r
+ */\r
+ uint32_t ulTruncIdx = ulDeleteIdx - 1U;\r
bool fDone = false;\r
\r
/* We are deleting the last dirent in the directory, so search\r
backwards to find the last populated dirent, allowing us to truncate\r
the directory to that point.\r
*/\r
- while((ret == 0) && (ulTruncIdx > 0U) && !fDone)\r
+ while((ret == 0) && (ulTruncIdx != UINT32_MAX) && !fDone)\r
{\r
ret = RedInodeDataSeekAndRead(pPInode, ulTruncIdx / DIRENTS_PER_BLOCK);\r
\r
if(ret == 0)\r
{\r
const DIRENT *pDirents = CAST_CONST_DIRENT_PTR(pPInode->pbData);\r
- uint32_t ulBlockIdx = (ulTruncIdx - 1U) % DIRENTS_PER_BLOCK;\r
+ uint32_t ulBlockIdx = ulTruncIdx % DIRENTS_PER_BLOCK;\r
\r
do\r
{\r
}\r
}\r
\r
+ /* Currently ulTruncIdx represents the last valid dirent index, or\r
+ UINT32_MAX if the directory is now empty. Increment it so that it\r
+ represents the first invalid entry, which will be truncated.\r
+ */\r
+ ulTruncIdx++;\r
+\r
/* Truncate the directory, deleting the requested entry and any empty\r
dirents at the end of the directory.\r
*/\r
#if (REDCONF_API_POSIX_READDIR == 1) || (REDCONF_CHECKER == 1)\r
/** @brief Read the next entry from a directory, given a starting index.\r
\r
- @param pInode A pointer to the cached inode structure of the directory to\r
+ @param pPInode A pointer to the cached inode structure of the directory to\r
read from.\r
@param pulIdx On entry, the directory index to start reading from. On\r
successful return, populated with the directory index to use\r
}\r
\r
/* Adjust the free/almost free block count if the block was allocable.\r
+ Discard the block if required.\r
*/\r
if((ret == 0) && (ulBlock >= gpRedCoreVol->ulFirstAllocableBN))\r
{\r
\r
if(ret != 0)\r
{\r
+ /* If we fail to mount, invalidate the buffers to prevent any\r
+ confusion that could be caused by stale or corrupt metadata.\r
+ */\r
+ (void)RedBufferDiscardRange(0U, gpRedVolume->ulBlockCount);\r
(void)RedOsBDevClose(gbRedVolNum);\r
}\r
}\r
## Release History and Changes
+### Reliance Edge v2.0, January 2017
+
+- Added support for Linux as a host environment
+ - All "host" projects may now be built in either Windows or Linux using the
+ `make` command. The formatter and image builder are built, and the checker
+ and image copier are also built in the commercial kit.
+ - An additional host tool has been added for Linux only: `redfuse`. It is a
+ File System in User Space (FUSE) implementation, allowing a Reliance Edge
+ volume to be mounted directly on Linux for easy access. It is built from
+ the host project folder using the command `make redfuse`.
+ - The OS-specific API test (commercial kit only) is now ported to run on Linux
+ for the purpose of verifying the FUSE implementation.
+- Fixed a bug that could leave a directory in an invalid state after removing
+ files. For example, an affected directory might report a non-zero length even
+ after all files had been deleted.
+- Fixed a bug that would leave the driver in a bad state if a mount operation
+ failed due to missing or corrupt metaroot blocks.
+
+### Reliance Edge v1.1 (Beta), November 2016
+
+- Added support for a discard (trim) interface in the commercial kit. While
+ discards are not integral to the behavior of the filesystem, they allow
+ certain types of Flash drivers and media to perform at optimal speed and
+ efficiency. The commercial version of Reliance Edge now allows the user to
+ implement this interface for compatible storage media.
+ - This change added new fields to the configuration files redconf.h and
+ redconf.c. The configuration utility has been updated to version 1.1 and
+ existing configuration files must be updated using the updated utility.
+- The configuration utility now has keyboard shortcuts for opening and saving
+ the configuration.
+- The configuration utility now adds version macros to easily identify when an
+ outdated configuration file is used with Reliance Edge or vice versa.
+
### Reliance Edge v1.0.4, July 2016
- Added ARM mbed and ARM mbed OS support in the commercial kit, with an example
number of times to retry a block device read, write or flush operation before
returning a failure. The configuration tool has been updated to version 1.0.2
with this change.
- - This added a new field to the volume configuration in to redconf.c: existing
+ - This added a new field to the volume configuration in redconf.c: existing
redconf.c files from v1.0.1 and earlier must be updated to work with v1.0.2.
Open redconf.h and redconf.c with the configuration tool, enable
"Retry block device I/O on failure" for any volumes if desired, and save the
Release History and Changes
+Reliance Edge v2.0, January 2017
+
+- Added support for Linux as a host environment
+- All "host" projects may now be built in either Windows or Linux
+ using the make command. The formatter and image builder are built,
+ and the checker and image copier are also built in the
+ commercial kit.
+- An additional host tool has been added for Linux only: redfuse. It
+ is a File System in User Space (FUSE) implementation, allowing a
+ Reliance Edge volume to be mounted directly on Linux for
+ easy access. It is built from the host project folder using the
+ command make redfuse.
+- The OS-specific API test (commercial kit only) is now ported to run
+ on Linux for the purpose of verifying the FUSE implementation.
+- Fixed a bug that could leave a directory in an invalid state after
+ removing files. For example, an affected directory might report a
+ non-zero length even after all files had been deleted.
+- Fixed a bug that would leave the driver in a bad state if a mount
+ operation failed due to missing or corrupt metaroot blocks.
+
+Reliance Edge v1.1 (Beta), November 2016
+
+- Added support for a discard (trim) interface in the commercial kit.
+ While discards are not integral to the behavior of the filesystem,
+ they allow certain types of Flash drivers and media to perform at
+ optimal speed and efficiency. The commercial version of Reliance
+ Edge now allows the user to implement this interface for compatible
+ storage media.
+- This change added new fields to the configuration files redconf.h
+ and redconf.c. The configuration utility has been updated to version
+ 1.1 and existing configuration files must be updated using the
+ updated utility.
+- The configuration utility now has keyboard shortcuts for opening and
+ saving the configuration.
+- The configuration utility now adds version macros to easily identify
+ when an outdated configuration file is used with Reliance Edge or
+ vice versa.
+
Reliance Edge v1.0.4, July 2016
- Added ARM mbed and ARM mbed OS support in the commercial kit, with
specify a number of times to retry a block device read, write or
flush operation before returning a failure. The configuration tool
has been updated to version 1.0.2 with this change.
-- This added a new field to the volume configuration in to redconf.c:
+- This added a new field to the volume configuration in redconf.c:
existing redconf.c files from v1.0.1 and earlier must be updated to
work with v1.0.2. Open redconf.h and redconf.c with the
configuration tool, enable "Retry block device I/O on failure" for
-/* ----> DO NOT REMOVE THE FOLLOWING NOTICE <----\r
-\r
- Copyright (c) 2014-2015 Datalight, Inc.\r
- All Rights Reserved Worldwide.\r
-\r
- This program is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; use version 2 of the License.\r
-\r
- This program is distributed in the hope that it will be useful,\r
- but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty\r
- of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License along\r
- with this program; if not, write to the Free Software Foundation, Inc.,\r
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
-*/\r
-/* Businesses and individuals that for commercial or other reasons cannot\r
- comply with the terms of the GPLv2 license may obtain a commercial license\r
- before incorporating Reliance Edge into proprietary software for\r
- distribution in any form. Visit http://www.datalight.com/reliance-edge for\r
- more information.\r
-*/\r
-/** @file\r
- @brief Compile-time validity checks for the REDCONF macros.\r
-*/\r
-#ifndef REDCONFIGCHK_H\r
-#define REDCONFIGCHK_H\r
-\r
-#ifndef REDCONF_READ_ONLY\r
- #error "Configuration error: REDCONF_READ_ONLY must be defined."\r
-#endif\r
-#ifndef REDCONF_API_POSIX\r
- #error "Configuration error: REDCONF_API_POSIX must be defined."\r
-#endif\r
-#ifndef REDCONF_API_FSE\r
- #error "Configuration error: REDCONF_API_FSE must be defined."\r
-#endif\r
-\r
-#if REDCONF_API_POSIX == 1\r
- #ifndef REDCONF_API_POSIX_FORMAT\r
- #error "Configuration error: REDCONF_API_POSIX_FORMAT must be defined."\r
- #endif\r
- #ifndef REDCONF_API_POSIX_UNLINK\r
- #error "Configuration error: REDCONF_API_POSIX_UNLINK must be defined."\r
- #endif\r
- #ifndef REDCONF_API_POSIX_MKDIR\r
- #error "Configuration error: REDCONF_API_POSIX_MKDIR must be defined."\r
- #endif\r
- #ifndef REDCONF_API_POSIX_RMDIR\r
- #error "Configuration error: REDCONF_API_POSIX_RMDIR must be defined."\r
- #endif\r
- #ifndef REDCONF_API_POSIX_RENAME\r
- #error "Configuration error: REDCONF_API_POSIX_RENAME must be defined."\r
- #endif\r
- #ifndef REDCONF_API_POSIX_LINK\r
- #error "Configuration error: REDCONF_API_POSIX_LINK must be defined."\r
- #endif\r
- #ifndef REDCONF_API_POSIX_FTRUNCATE\r
- #error "Configuration error: REDCONF_API_POSIX_FTRUNCATE must be defined."\r
- #endif\r
- #ifndef REDCONF_API_POSIX_READDIR\r
- #error "Configuration error: REDCONF_API_POSIX_READDIR must be defined."\r
- #endif\r
- #ifndef REDCONF_NAME_MAX\r
- #error "Configuration error: REDCONF_NAME_MAX must be defined."\r
- #endif\r
- #ifndef REDCONF_PATH_SEPARATOR\r
- #error "Configuration error: REDCONF_PATH_SEPARATOR must be defined."\r
- #endif\r
- #ifndef REDCONF_RENAME_ATOMIC\r
- #error "Configuration error: REDCONF_RENAME_ATOMIC must be defined."\r
- #endif\r
- #ifndef REDCONF_HANDLE_COUNT\r
- #error "Configuration error: REDCONF_HANDLE_COUNT must be defined."\r
- #endif\r
-#endif\r
-#if REDCONF_API_FSE == 1\r
- #ifndef REDCONF_API_FSE_FORMAT\r
- #error "Configuration error: REDCONF_API_FSE_FORMAT must be defined."\r
- #endif\r
- #ifndef REDCONF_API_FSE_TRUNCATE\r
- #error "Configuration error: REDCONF_API_FSE_TRUNCATE must be defined."\r
- #endif\r
- #ifndef REDCONF_API_FSE_TRANSMASKSET\r
- #error "Configuration error: REDCONF_API_FSE_TRANSMASKSET must be defined."\r
- #endif\r
- #ifndef REDCONF_API_FSE_TRANSMASKGET\r
- #error "Configuration error: REDCONF_API_FSE_TRANSMASKGET must be defined."\r
- #endif\r
-#endif\r
-\r
-#ifndef REDCONF_TASK_COUNT\r
- #error "Configuration error: REDCONF_TASK_COUNT must be defined."\r
-#endif\r
-#ifndef REDCONF_ENDIAN_BIG\r
- #error "Configuration error: REDCONF_ENDIAN_BIG must be defined."\r
-#endif\r
-#ifndef REDCONF_ALIGNMENT_SIZE\r
- #error "Configuration error: REDCONF_ALIGNMENT_SIZE must be defined."\r
-#endif\r
-#ifndef REDCONF_CRC_ALGORITHM\r
- #error "Configuration error: REDCONF_CRC_ALGORITHM must be defined."\r
-#endif\r
-#ifndef REDCONF_INODE_TIMESTAMPS\r
- #error "Configuration error: REDCONF_INODE_TIMESTAMPS must be defined."\r
-#endif\r
-#ifndef REDCONF_ATIME\r
- #error "Configuration error: REDCONF_ATIME must be defined."\r
-#endif\r
-#ifndef REDCONF_DIRECT_POINTERS\r
- #error "Configuration error: REDCONF_DIRECT_POINTERS must be defined."\r
-#endif\r
-#ifndef REDCONF_INDIRECT_POINTERS\r
- #error "Configuration error: REDCONF_INDIRECT_POINTERS must be defined."\r
-#endif\r
-#ifndef REDCONF_INODE_BLOCKS\r
- #error "Configuration error: REDCONF_INODE_BLOCKS must be defined."\r
-#endif\r
-#ifndef REDCONF_IMAP_EXTERNAL\r
- #error "Configuration error: REDCONF_IMAP_EXTERNAL must be defined."\r
-#endif\r
-#ifndef REDCONF_IMAP_INLINE\r
- #error "Configuration error: REDCONF_IMAP_INLINE must be defined."\r
-#endif\r
-#ifndef REDCONF_OUTPUT\r
- #error "Configuration error: REDCONF_OUTPUT must be defined."\r
-#endif\r
-#ifndef REDCONF_ASSERTS\r
- #error "Configuration error: REDCONF_ASSERTS must be defined."\r
-#endif\r
-#ifndef REDCONF_TRANSACT_DEFAULT\r
- #error "Configuration error: REDCONF_TRANSACT_DEFAULT must be defined."\r
-#endif\r
-#ifndef REDCONF_BUFFER_COUNT\r
- #error "Configuration error: REDCONF_BUFFER_COUNT must be defined."\r
-#endif\r
-#ifndef REDCONF_BLOCK_SIZE\r
- #error "Configuration error: REDCONF_BLOCK_SIZE must be defined."\r
-#endif\r
-#ifndef REDCONF_VOLUME_COUNT\r
- #error "Configuration error: REDCONF_VOLUME_COUNT must be defined."\r
-#endif\r
-#ifndef REDCONF_IMAGE_BUILDER\r
- #error "Configuration error: REDCONF_IMAGE_BUILDER must be defined."\r
-#endif\r
-#ifndef REDCONF_CHECKER\r
- #error "Configuration error: REDCONF_CHECKER must be defined."\r
-#endif\r
-\r
-\r
-#if (REDCONF_READ_ONLY != 0) && (REDCONF_READ_ONLY != 1)\r
- #error "Configuration error: REDCONF_READ_ONLY must be either 0 or 1"\r
-#endif\r
-\r
-#if (REDCONF_API_POSIX != 0) && (REDCONF_API_POSIX != 1)\r
- #error "Configuration error: REDCONF_API_POSIX must be either 0 or 1."\r
-#endif\r
-#if (REDCONF_API_FSE != 0) && (REDCONF_API_FSE != 1)\r
- #error "Configuration error: REDCONF_API_FSE must be either 0 or 1."\r
-#endif\r
-\r
-#if (REDCONF_API_FSE == 0) && (REDCONF_API_POSIX == 0)\r
- #error "Configuration error: either REDCONF_API_FSE or REDCONF_API_POSIX must be set to 1."\r
-#endif\r
-\r
-#if REDCONF_API_POSIX == 1\r
- #if REDCONF_API_FSE != 0\r
- #error "Configuration error: REDCONF_API_FSE must be 0 if REDCONF_API_POSIX is 1"\r
- #endif\r
-\r
- #if (REDCONF_API_POSIX_FORMAT != 0) && (REDCONF_API_POSIX_FORMAT != 1)\r
- #error "Configuration error: REDCONF_API_POSIX_FORMAT must be either 0 or 1."\r
- #endif\r
-\r
- #if (REDCONF_API_POSIX_UNLINK != 0) && (REDCONF_API_POSIX_UNLINK != 1)\r
- #error "Configuration error: REDCONF_API_POSIX_UNLINK must be either 0 or 1."\r
- #endif\r
-\r
- #if (REDCONF_API_POSIX_MKDIR != 0) && (REDCONF_API_POSIX_MKDIR != 1)\r
- #error "Configuration error: REDCONF_API_POSIX_MKDIR must be either 0 or 1."\r
- #endif\r
-\r
- #if (REDCONF_API_POSIX_RMDIR != 0) && (REDCONF_API_POSIX_RMDIR != 1)\r
- #error "Configuration error: REDCONF_API_POSIX_RMDIR must be either 0 or 1."\r
- #endif\r
-\r
- #if (REDCONF_API_POSIX_RENAME != 0) && (REDCONF_API_POSIX_RENAME != 1)\r
- #error "Configuration error: REDCONF_API_POSIX_RENAME must be either 0 or 1."\r
- #endif\r
-\r
- #if (REDCONF_API_POSIX_LINK != 0) && (REDCONF_API_POSIX_LINK != 1)\r
- #error "Configuration error: REDCONF_API_POSIX_LINK must be either 0 or 1."\r
- #endif\r
-\r
- #if (REDCONF_API_POSIX_FTRUNCATE != 0) && (REDCONF_API_POSIX_FTRUNCATE != 1)\r
- #error "Configuration error: REDCONF_API_POSIX_FTRUNCATE must be either 0 or 1."\r
- #endif\r
-\r
- #if (REDCONF_API_POSIX_READDIR != 0) && (REDCONF_API_POSIX_READDIR != 1)\r
- #error "Configuration error: REDCONF_API_POSIX_READDIR must be either 0 or 1."\r
- #endif\r
-\r
- #if (REDCONF_NAME_MAX < 1U) || (REDCONF_NAME_MAX > (REDCONF_BLOCK_SIZE - 4U))\r
- #error "Configuration error: invalid value of REDCONF_NAME_MAX"\r
- #endif\r
-\r
- #if (REDCONF_PATH_SEPARATOR < 1) || (REDCONF_PATH_SEPARATOR > 127)\r
- #error "Configuration error: invalid value of REDCONF_PATH_SEPARATOR"\r
- #endif\r
-\r
- #if (REDCONF_RENAME_ATOMIC != 0) && (REDCONF_RENAME_ATOMIC != 1)\r
- #error "Configuration error: REDCONF_RENAME_ATOMIC must be either 0 or 1."\r
- #endif\r
-\r
- #if (REDCONF_HANDLE_COUNT < 1U) || (REDCONF_HANDLE_COUNT > 4096U)\r
- #error "Configuration error: invalid value of REDCONF_HANDLE_COUNT"\r
- #endif\r
-#endif\r
-#if REDCONF_API_FSE == 1\r
- #if (REDCONF_API_FSE_FORMAT != 0) && (REDCONF_API_FSE_FORMAT != 1)\r
- #error "Configuration error: REDCONF_API_FSE_FORMAT must be either 0 or 1."\r
- #endif\r
-\r
- #if (REDCONF_API_FSE_TRUNCATE != 0) && (REDCONF_API_FSE_TRUNCATE != 1)\r
- #error "Configuration error: REDCONF_API_FSE_TRUNCATE must be either 0 or 1."\r
- #endif\r
-\r
- #if (REDCONF_API_FSE_TRANSMASKSET != 0) && (REDCONF_API_FSE_TRANSMASKSET != 1)\r
- #error "Configuration error: REDCONF_API_FSE_TRANSMASKSET must be either 0 or 1."\r
- #endif\r
-\r
- #if (REDCONF_API_FSE_TRANSMASKGET != 0) && (REDCONF_API_FSE_TRANSMASKGET != 1)\r
- #error "Configuration error: REDCONF_API_FSE_TRANSMASKGET must be either 0 or 1."\r
- #endif\r
-#endif\r
-\r
-#if REDCONF_TASK_COUNT < 1U\r
- #error "Configuration error: invalid value of REDCONF_TASK_COUNT"\r
-#endif\r
-\r
-#if (REDCONF_ENDIAN_BIG != 0) && (REDCONF_ENDIAN_BIG != 1)\r
- #error "Configuration error: REDCONF_ENDIAN_BIG must be either 0 or 1."\r
-#endif\r
-\r
-#if (REDCONF_ALIGNMENT_SIZE != 1U) && (REDCONF_ALIGNMENT_SIZE != 2U) && (REDCONF_ALIGNMENT_SIZE != 4U) && (REDCONF_ALIGNMENT_SIZE != 8U)\r
- #error "Configuration error: invalid value REDCONF_ALIGNMENT_SIZE"\r
-#endif\r
-\r
-/* REDCONF_CRC_ALGORITHM checked in crc.c\r
-*/\r
-\r
-#if (REDCONF_INODE_TIMESTAMPS != 0) && (REDCONF_INODE_TIMESTAMPS != 1)\r
- #error "Configuration error: REDCONF_INODE_TIMESTAMPS must be either 0 or 1."\r
-#endif\r
-\r
-#if (REDCONF_ATIME != 0) && (REDCONF_ATIME != 1)\r
- #error "Configuration error: REDCONF_ATIME must be either 0 or 1."\r
-#endif\r
-\r
-#if (REDCONF_INODE_TIMESTAMPS == 0) && (REDCONF_ATIME == 1)\r
- #error "Configuration error: REDCONF_ATIME must be 0 when REDCONF_INODE_TIMESTAMPS is 0."\r
-#endif\r
-\r
-/* REDCONF_DIRECT_POINTERS and REDCONF_INDIRECT_POINTERS checked in rednodes.h\r
-*/\r
-\r
-#if (REDCONF_INODE_BLOCKS != 0) && (REDCONF_INODE_BLOCKS != 1)\r
- #error "Configuration error: REDCONF_INODE_BLOCKS must be either 0 or 1."\r
-#endif\r
-\r
-/* Further validity checking of imap specs done in RelCoreInit()\r
-*/\r
-#if (REDCONF_IMAP_EXTERNAL != 0) && (REDCONF_IMAP_EXTERNAL != 1)\r
- #error "Configuration error: REDCONF_IMAP_EXTERNAL must be either 0 or 1."\r
-#endif\r
-#if (REDCONF_IMAP_INLINE != 0) && (REDCONF_IMAP_INLINE != 1)\r
- #error "Configuration error: REDCONF_IMAP_INLINE must be either 0 or 1."\r
-#endif\r
-#if (REDCONF_IMAP_INLINE == 0) && (REDCONF_IMAP_EXTERNAL == 0)\r
- #error "Configuration error: At least one of REDCONF_IMAP_INLINE and REDCONF_IMAP_EXTERNAL must be set"\r
-#endif\r
-\r
-#if (REDCONF_OUTPUT != 0) && (REDCONF_OUTPUT != 1)\r
- #error "Configuration error: REDCONF_OUTPUT must be either 0 or 1."\r
-#endif\r
-\r
-#if (REDCONF_ASSERTS != 0) && (REDCONF_ASSERTS != 1)\r
- #error "Configuration error: REDCONF_ASSERTS must be either 0 or 1."\r
-#endif\r
-\r
-/* REDCONF_BLOCK_SIZE checked in redmacs.h\r
-*/\r
-\r
-#if (REDCONF_VOLUME_COUNT < 1U) || (REDCONF_VOLUME_COUNT > 255U)\r
- #error "REDCONF_VOLUME_COUNT must be an integer between 1 and 255"\r
-#endif\r
-\r
-/* REDCONF_BUFFER_COUNT lower limit checked in buffer.c\r
-*/\r
-#if REDCONF_BUFFER_COUNT > 255U\r
- #error "REDCONF_BUFFER_COUNT cannot be greater than 255"\r
-#endif\r
-\r
-#if (REDCONF_IMAGE_BUILDER != 0) && (REDCONF_IMAGE_BUILDER != 1)\r
- #error "Configuration error: REDCONF_IMAGE_BUILDER must be either 0 or 1."\r
-#endif\r
-\r
-#if (REDCONF_CHECKER != 0) && (REDCONF_CHECKER != 1)\r
- #error "Configuration error: REDCONF_CHECKER must be either 0 or 1."\r
-#endif\r
-\r
-\r
-#endif\r
-\r
-\r
+/* ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
+
+ Copyright (c) 2014-2015 Datalight, Inc.
+ All Rights Reserved Worldwide.
+
+ 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; use version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but "AS-IS," 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+/* Businesses and individuals that for commercial or other reasons cannot
+ comply with the terms of the GPLv2 license may obtain a commercial license
+ before incorporating Reliance Edge into proprietary software for
+ distribution in any form. Visit http://www.datalight.com/reliance-edge for
+ more information.
+*/
+/** @file
+ @brief Compile-time validity checks for the REDCONF macros.
+*/
+#ifndef REDCONFIGCHK_H
+#define REDCONFIGCHK_H
+
+#ifdef RED_CONFIG_MINCOMPAT_VER
+ #if (RED_VERSION_VAL & 0xFFFFFF00U) < (RED_CONFIG_MINCOMPAT_VER & 0xFFFFFF00U)
+ /* This indicates that your configuration files were generated by a
+ version of the Reliance Edge Configuration Utility that is designed
+ for a more recent version of Reliance Edge and is no longer compatible
+ with this version. You can update to the most recent version of
+ Reliance Edge or contact RelianceEdgeSupport@datalight.com to obtain
+ the correct legacy version of the Configuration Utility.
+ */
+ #error "Your configuration is not compatible with this version of Reliance Edge. Please download the latest version of Reliance Edge or recreate your configuration with an older version of the Configuration Utility."
+ #endif
+#endif
+
+#ifndef REDCONF_READ_ONLY
+ #error "Configuration error: REDCONF_READ_ONLY must be defined."
+#endif
+#ifndef REDCONF_API_POSIX
+ #error "Configuration error: REDCONF_API_POSIX must be defined."
+#endif
+#ifndef REDCONF_API_FSE
+ #error "Configuration error: REDCONF_API_FSE must be defined."
+#endif
+
+#if REDCONF_API_POSIX == 1
+ #ifndef REDCONF_API_POSIX_FORMAT
+ #error "Configuration error: REDCONF_API_POSIX_FORMAT must be defined."
+ #endif
+ #ifndef REDCONF_API_POSIX_UNLINK
+ #error "Configuration error: REDCONF_API_POSIX_UNLINK must be defined."
+ #endif
+ #ifndef REDCONF_API_POSIX_MKDIR
+ #error "Configuration error: REDCONF_API_POSIX_MKDIR must be defined."
+ #endif
+ #ifndef REDCONF_API_POSIX_RMDIR
+ #error "Configuration error: REDCONF_API_POSIX_RMDIR must be defined."
+ #endif
+ #ifndef REDCONF_API_POSIX_RENAME
+ #error "Configuration error: REDCONF_API_POSIX_RENAME must be defined."
+ #endif
+ #ifndef REDCONF_API_POSIX_LINK
+ #error "Configuration error: REDCONF_API_POSIX_LINK must be defined."
+ #endif
+ #ifndef REDCONF_API_POSIX_FTRUNCATE
+ #error "Configuration error: REDCONF_API_POSIX_FTRUNCATE must be defined."
+ #endif
+ #ifndef REDCONF_API_POSIX_READDIR
+ #error "Configuration error: REDCONF_API_POSIX_READDIR must be defined."
+ #endif
+ #ifndef REDCONF_NAME_MAX
+ #error "Configuration error: REDCONF_NAME_MAX must be defined."
+ #endif
+ #ifndef REDCONF_PATH_SEPARATOR
+ #error "Configuration error: REDCONF_PATH_SEPARATOR must be defined."
+ #endif
+ #ifndef REDCONF_RENAME_ATOMIC
+ #error "Configuration error: REDCONF_RENAME_ATOMIC must be defined."
+ #endif
+ #ifndef REDCONF_HANDLE_COUNT
+ #error "Configuration error: REDCONF_HANDLE_COUNT must be defined."
+ #endif
+#endif
+#if REDCONF_API_FSE == 1
+ #ifndef REDCONF_API_FSE_FORMAT
+ #error "Configuration error: REDCONF_API_FSE_FORMAT must be defined."
+ #endif
+ #ifndef REDCONF_API_FSE_TRUNCATE
+ #error "Configuration error: REDCONF_API_FSE_TRUNCATE must be defined."
+ #endif
+ #ifndef REDCONF_API_FSE_TRANSMASKSET
+ #error "Configuration error: REDCONF_API_FSE_TRANSMASKSET must be defined."
+ #endif
+ #ifndef REDCONF_API_FSE_TRANSMASKGET
+ #error "Configuration error: REDCONF_API_FSE_TRANSMASKGET must be defined."
+ #endif
+#endif
+
+#ifndef REDCONF_TASK_COUNT
+ #error "Configuration error: REDCONF_TASK_COUNT must be defined."
+#endif
+#ifndef REDCONF_ENDIAN_BIG
+ #error "Configuration error: REDCONF_ENDIAN_BIG must be defined."
+#endif
+#ifndef REDCONF_ALIGNMENT_SIZE
+ #error "Configuration error: REDCONF_ALIGNMENT_SIZE must be defined."
+#endif
+#ifndef REDCONF_CRC_ALGORITHM
+ #error "Configuration error: REDCONF_CRC_ALGORITHM must be defined."
+#endif
+#ifndef REDCONF_INODE_TIMESTAMPS
+ #error "Configuration error: REDCONF_INODE_TIMESTAMPS must be defined."
+#endif
+#ifndef REDCONF_ATIME
+ #error "Configuration error: REDCONF_ATIME must be defined."
+#endif
+#ifndef REDCONF_DIRECT_POINTERS
+ #error "Configuration error: REDCONF_DIRECT_POINTERS must be defined."
+#endif
+#ifndef REDCONF_INDIRECT_POINTERS
+ #error "Configuration error: REDCONF_INDIRECT_POINTERS must be defined."
+#endif
+#ifndef REDCONF_INODE_BLOCKS
+ #error "Configuration error: REDCONF_INODE_BLOCKS must be defined."
+#endif
+#ifndef REDCONF_IMAP_EXTERNAL
+ #error "Configuration error: REDCONF_IMAP_EXTERNAL must be defined."
+#endif
+#ifndef REDCONF_IMAP_INLINE
+ #error "Configuration error: REDCONF_IMAP_INLINE must be defined."
+#endif
+#ifndef REDCONF_OUTPUT
+ #error "Configuration error: REDCONF_OUTPUT must be defined."
+#endif
+#ifndef REDCONF_ASSERTS
+ #error "Configuration error: REDCONF_ASSERTS must be defined."
+#endif
+#ifndef REDCONF_TRANSACT_DEFAULT
+ #error "Configuration error: REDCONF_TRANSACT_DEFAULT must be defined."
+#endif
+#ifndef REDCONF_BUFFER_COUNT
+ #error "Configuration error: REDCONF_BUFFER_COUNT must be defined."
+#endif
+#ifndef REDCONF_BLOCK_SIZE
+ #error "Configuration error: REDCONF_BLOCK_SIZE must be defined."
+#endif
+#ifndef REDCONF_VOLUME_COUNT
+ #error "Configuration error: REDCONF_VOLUME_COUNT must be defined."
+#endif
+#ifndef REDCONF_DISCARDS
+ /* Reliance Edge 1.0.5 and below did not have REDCONF_DISCARDS. You can
+ fix this error by downloading the latest version of the Configuration
+ Utility (assuming you are using the latest version of Reliance Edge)
+ from http://www.datalight.com/reliance-edge, loading your redconf.c
+ and redconf.h files, and saving them again, replacing the original
+ files.
+ */
+ #error "Configuration error: your redconf.h is not compatible. Update your redconf files with a compatible version of the configuration utility."
+#endif
+#ifndef REDCONF_IMAGE_BUILDER
+ #error "Configuration error: REDCONF_IMAGE_BUILDER must be defined."
+#endif
+#ifndef REDCONF_CHECKER
+ #error "Configuration error: REDCONF_CHECKER must be defined."
+#endif
+
+
+#if (REDCONF_READ_ONLY != 0) && (REDCONF_READ_ONLY != 1)
+ #error "Configuration error: REDCONF_READ_ONLY must be either 0 or 1"
+#endif
+
+#if (REDCONF_API_POSIX != 0) && (REDCONF_API_POSIX != 1)
+ #error "Configuration error: REDCONF_API_POSIX must be either 0 or 1."
+#endif
+#if (REDCONF_API_FSE != 0) && (REDCONF_API_FSE != 1)
+ #error "Configuration error: REDCONF_API_FSE must be either 0 or 1."
+#endif
+
+#if (REDCONF_API_FSE == 0) && (REDCONF_API_POSIX == 0)
+ #error "Configuration error: either REDCONF_API_FSE or REDCONF_API_POSIX must be set to 1."
+#endif
+
+#if REDCONF_API_POSIX == 1
+ #if REDCONF_API_FSE != 0
+ #error "Configuration error: REDCONF_API_FSE must be 0 if REDCONF_API_POSIX is 1"
+ #endif
+
+ #if (REDCONF_API_POSIX_FORMAT != 0) && (REDCONF_API_POSIX_FORMAT != 1)
+ #error "Configuration error: REDCONF_API_POSIX_FORMAT must be either 0 or 1."
+ #endif
+
+ #if (REDCONF_API_POSIX_UNLINK != 0) && (REDCONF_API_POSIX_UNLINK != 1)
+ #error "Configuration error: REDCONF_API_POSIX_UNLINK must be either 0 or 1."
+ #endif
+
+ #if (REDCONF_API_POSIX_MKDIR != 0) && (REDCONF_API_POSIX_MKDIR != 1)
+ #error "Configuration error: REDCONF_API_POSIX_MKDIR must be either 0 or 1."
+ #endif
+
+ #if (REDCONF_API_POSIX_RMDIR != 0) && (REDCONF_API_POSIX_RMDIR != 1)
+ #error "Configuration error: REDCONF_API_POSIX_RMDIR must be either 0 or 1."
+ #endif
+
+ #if (REDCONF_API_POSIX_RENAME != 0) && (REDCONF_API_POSIX_RENAME != 1)
+ #error "Configuration error: REDCONF_API_POSIX_RENAME must be either 0 or 1."
+ #endif
+
+ #if (REDCONF_API_POSIX_LINK != 0) && (REDCONF_API_POSIX_LINK != 1)
+ #error "Configuration error: REDCONF_API_POSIX_LINK must be either 0 or 1."
+ #endif
+
+ #if (REDCONF_API_POSIX_FTRUNCATE != 0) && (REDCONF_API_POSIX_FTRUNCATE != 1)
+ #error "Configuration error: REDCONF_API_POSIX_FTRUNCATE must be either 0 or 1."
+ #endif
+
+ #if (REDCONF_API_POSIX_READDIR != 0) && (REDCONF_API_POSIX_READDIR != 1)
+ #error "Configuration error: REDCONF_API_POSIX_READDIR must be either 0 or 1."
+ #endif
+
+ #if (REDCONF_NAME_MAX < 1U) || (REDCONF_NAME_MAX > (REDCONF_BLOCK_SIZE - 4U))
+ #error "Configuration error: invalid value of REDCONF_NAME_MAX"
+ #endif
+
+ #if (REDCONF_PATH_SEPARATOR < 1) || (REDCONF_PATH_SEPARATOR > 127)
+ #error "Configuration error: invalid value of REDCONF_PATH_SEPARATOR"
+ #endif
+
+ #if (REDCONF_RENAME_ATOMIC != 0) && (REDCONF_RENAME_ATOMIC != 1)
+ #error "Configuration error: REDCONF_RENAME_ATOMIC must be either 0 or 1."
+ #endif
+
+ #if (REDCONF_HANDLE_COUNT < 1U) || (REDCONF_HANDLE_COUNT > 4096U)
+ #error "Configuration error: invalid value of REDCONF_HANDLE_COUNT"
+ #endif
+#endif
+#if REDCONF_API_FSE == 1
+ #if (REDCONF_API_FSE_FORMAT != 0) && (REDCONF_API_FSE_FORMAT != 1)
+ #error "Configuration error: REDCONF_API_FSE_FORMAT must be either 0 or 1."
+ #endif
+
+ #if (REDCONF_API_FSE_TRUNCATE != 0) && (REDCONF_API_FSE_TRUNCATE != 1)
+ #error "Configuration error: REDCONF_API_FSE_TRUNCATE must be either 0 or 1."
+ #endif
+
+ #if (REDCONF_API_FSE_TRANSMASKSET != 0) && (REDCONF_API_FSE_TRANSMASKSET != 1)
+ #error "Configuration error: REDCONF_API_FSE_TRANSMASKSET must be either 0 or 1."
+ #endif
+
+ #if (REDCONF_API_FSE_TRANSMASKGET != 0) && (REDCONF_API_FSE_TRANSMASKGET != 1)
+ #error "Configuration error: REDCONF_API_FSE_TRANSMASKGET must be either 0 or 1."
+ #endif
+#endif
+
+#if REDCONF_TASK_COUNT < 1U
+ #error "Configuration error: invalid value of REDCONF_TASK_COUNT"
+#endif
+
+#if (REDCONF_ENDIAN_BIG != 0) && (REDCONF_ENDIAN_BIG != 1)
+ #error "Configuration error: REDCONF_ENDIAN_BIG must be either 0 or 1."
+#endif
+
+#if (REDCONF_ALIGNMENT_SIZE != 1U) && (REDCONF_ALIGNMENT_SIZE != 2U) && (REDCONF_ALIGNMENT_SIZE != 4U) && (REDCONF_ALIGNMENT_SIZE != 8U)
+ #error "Configuration error: invalid value REDCONF_ALIGNMENT_SIZE"
+#endif
+
+/* REDCONF_CRC_ALGORITHM checked in crc.c
+*/
+
+#if (REDCONF_INODE_TIMESTAMPS != 0) && (REDCONF_INODE_TIMESTAMPS != 1)
+ #error "Configuration error: REDCONF_INODE_TIMESTAMPS must be either 0 or 1."
+#endif
+
+#if (REDCONF_ATIME != 0) && (REDCONF_ATIME != 1)
+ #error "Configuration error: REDCONF_ATIME must be either 0 or 1."
+#endif
+
+#if (REDCONF_INODE_TIMESTAMPS == 0) && (REDCONF_ATIME == 1)
+ #error "Configuration error: REDCONF_ATIME must be 0 when REDCONF_INODE_TIMESTAMPS is 0."
+#endif
+
+/* REDCONF_DIRECT_POINTERS and REDCONF_INDIRECT_POINTERS checked in rednodes.h
+*/
+
+#if (REDCONF_INODE_BLOCKS != 0) && (REDCONF_INODE_BLOCKS != 1)
+ #error "Configuration error: REDCONF_INODE_BLOCKS must be either 0 or 1."
+#endif
+
+/* Further validity checking of imap specs done in RelCoreInit()
+*/
+#if (REDCONF_IMAP_EXTERNAL != 0) && (REDCONF_IMAP_EXTERNAL != 1)
+ #error "Configuration error: REDCONF_IMAP_EXTERNAL must be either 0 or 1."
+#endif
+#if (REDCONF_IMAP_INLINE != 0) && (REDCONF_IMAP_INLINE != 1)
+ #error "Configuration error: REDCONF_IMAP_INLINE must be either 0 or 1."
+#endif
+#if (REDCONF_IMAP_INLINE == 0) && (REDCONF_IMAP_EXTERNAL == 0)
+ #error "Configuration error: At least one of REDCONF_IMAP_INLINE and REDCONF_IMAP_EXTERNAL must be set"
+#endif
+
+#if (REDCONF_OUTPUT != 0) && (REDCONF_OUTPUT != 1)
+ #error "Configuration error: REDCONF_OUTPUT must be either 0 or 1."
+#endif
+
+#if (REDCONF_ASSERTS != 0) && (REDCONF_ASSERTS != 1)
+ #error "Configuration error: REDCONF_ASSERTS must be either 0 or 1."
+#endif
+
+/* REDCONF_BLOCK_SIZE checked in redmacs.h
+*/
+
+#if (REDCONF_VOLUME_COUNT < 1U) || (REDCONF_VOLUME_COUNT > 255U)
+ #error "REDCONF_VOLUME_COUNT must be an integer between 1 and 255"
+#endif
+
+#if (REDCONF_DISCARDS != 0) && (REDCONF_DISCARDS != 1)
+ #error "Configuration error: REDCONF_DISCARDS must be either 0 or 1."
+#endif
+
+/* REDCONF_BUFFER_COUNT lower limit checked in buffer.c
+*/
+#if REDCONF_BUFFER_COUNT > 255U
+ #error "REDCONF_BUFFER_COUNT cannot be greater than 255"
+#endif
+
+#if (REDCONF_IMAGE_BUILDER != 0) && (REDCONF_IMAGE_BUILDER != 1)
+ #error "Configuration error: REDCONF_IMAGE_BUILDER must be either 0 or 1."
+#endif
+
+#if (REDCONF_CHECKER != 0) && (REDCONF_CHECKER != 1)
+ #error "Configuration error: REDCONF_CHECKER must be either 0 or 1."
+#endif
+
+
+#if (REDCONF_DISCARDS == 1) && (RED_KIT == RED_KIT_GPL)
+ #error "REDCONF_DISCARDS not supported in Reliance Edge under GPL. Contact sales@datalight.com to upgrade."
+#endif
+
+
+#endif
+
+
|| ((REDCONF_API_FSE == 1) && (REDCONF_API_FSE_FORMAT == 1)) \\r
|| (REDCONF_IMAGE_BUILDER == 1)))\r
\r
-\r
#endif\r
\r
\r
\r
#include <redconf.h>\r
+#include "redver.h"\r
#include "redconfigchk.h"\r
#include <redtypes.h>\r
#include "rederrno.h"\r
#include "redutils.h"\r
#include "redosserv.h"\r
#include "redmisc.h"\r
-#include "redver.h"\r
#include "redexclude.h"\r
\r
\r
#define STOCH_POSIX_TEST_SUPPORTED \
( ((RED_KIT == RED_KIT_COMMERCIAL) || (RED_KIT == RED_KIT_SANDBOX)) \
&& (REDCONF_OUTPUT == 1) && (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX == 1) \
- && (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX_FORMAT == 1) && (REDCONF_API_POSIX_READDIR == 1) \
+ && (REDCONF_API_POSIX_FORMAT == 1) && (REDCONF_API_POSIX_READDIR == 1) \
&& (REDCONF_API_POSIX_MKDIR == 1) && (REDCONF_API_POSIX_RMDIR == 1) && (REDCONF_API_POSIX_UNLINK == 1) \
&& (REDCONF_API_POSIX_RENAME == 1))
\r
\r
uint8_t RedFindVolumeNumber(const char *pszVolume);\r
+bool RedConfirmOperation(const char *pszMessage);\r
\r
\r
#endif\r
--- /dev/null
+/* ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
+
+ Copyright (c) 2014-2015 Datalight, Inc.
+ All Rights Reserved Worldwide.
+
+ 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; use version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but "AS-IS," 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+/* Businesses and individuals that for commercial or other reasons cannot
+ comply with the terms of the GPLv2 license may obtain a commercial license
+ before incorporating Reliance Edge into proprietary software for
+ distribution in any form. Visit http://www.datalight.com/reliance-edge for
+ more information.
+*/
+#ifndef REDTOOLS_H
+#define REDTOOLS_H
+
+
+#ifdef _WIN32
+ #include <Windows.h>
+ #define HOST_PATH_MAX MAX_PATH
+#else
+ #include <linux/limits.h>
+ #define HOST_PATH_MAX PATH_MAX
+#endif
+
+
+#if REDCONF_IMAGE_BUILDER == 1
+
+#define MACRO_NAME_MAX_LEN 32
+
+typedef struct
+{
+ uint8_t bVolNumber;
+ const char *pszInputDir;
+ const char *pszOutputFile;
+ #if REDCONF_API_POSIX == 1
+ const char *pszVolName;
+ #else
+ const char *pszMapFile;
+ const char *pszDefineFile;
+ bool fNowarn;
+ #endif
+} IMGBLDPARAM;
+
+
+void ImgbldParseParams(int argc, char *argv [], IMGBLDPARAM *pParam);
+int ImgbldStart(IMGBLDPARAM *pParam);
+
+
+typedef struct
+{
+ #if REDCONF_API_POSIX == 1
+ char asOutFilePath[HOST_PATH_MAX];
+ #else
+ uint32_t ulOutFileIndex;
+ #endif
+ char asInFilePath[HOST_PATH_MAX];
+} FILEMAPPING;
+
+
+extern void *gpCopyBuffer;
+extern uint32_t gulCopyBufferSize;
+
+
+/* Implemented in ibposix.c
+*/
+#if REDCONF_API_POSIX == 1
+REDSTATUS IbPosixCopyDir(const char *pszVolName, const char *pszInDir);
+int IbPosixCreateDir(const char *pszVolName, const char *pszFullPath, const char *pszBasePath);
+int IbConvertPath(const char *pszVolName, const char *pszFullPath, const char *pszBasePath, char *szOutPath);
+#endif
+
+
+/* Implemented in ibfse.c
+*/
+#if REDCONF_API_FSE == 1
+typedef struct sFILELISTENTRY FILELISTENTRY;
+struct sFILELISTENTRY
+{
+ FILEMAPPING fileMapping;
+ FILELISTENTRY *pNext;
+};
+
+
+void FreeFileList(FILELISTENTRY **ppsFileList);
+
+int IbFseGetFileList(const char *pszPath, const char *pszIndirPath, FILELISTENTRY **ppFileListHead);
+int IbFseOutputDefines(FILELISTENTRY *pFileList, const IMGBLDPARAM *pOptions);
+int IbFseCopyFiles(int volNum, const FILELISTENTRY *pFileList);
+#endif
+
+
+/* Implemented in os-specific space (ibwin.c and iblinux.c)
+*/
+#if REDCONF_API_POSIX == 1
+int IbPosixCopyDirRecursive(const char *pszVolName, const char *pszInDir);
+#endif
+#if REDCONF_API_FSE == 1
+int IbFseBuildFileList(const char *pszDirPath, FILELISTENTRY **ppFileListHead);
+#endif
+#if REDCONF_API_FSE == 1
+int IbSetRelativePath(char *pszPath, const char *pszParentPath);
+#endif
+bool IsRegularFile(const char *pszPath);
+
+
+/* Implemented in ibcommon.c
+*/
+int IbCopyFile(int volNum, const FILEMAPPING *pFileMapping);
+int IbCheckFileExists(const char *pszPath, bool *pfExists);
+
+
+/* Implemented separately in ibfse.c and ibposix.c
+*/
+int IbApiInit(void);
+int IbApiUninit(void);
+int IbWriteFile(int volNum, const FILEMAPPING *pFileMapping, uint64_t ullOffset, void *pData, uint32_t ulDataLen);
+
+#endif /* IMAGE_BUILDER */
+
+/* For image copier tool
+*/
+
+#ifdef _WIN32
+ #define HOST_PSEP '\\'
+ #if !__STDC__
+ #define snprintf _snprintf
+ #define stat _stat
+ #define S_IFDIR _S_IFDIR
+ #define rmdir _rmdir
+ #endif
+#else
+ #define HOST_PSEP '/'
+#endif
+
+typedef struct
+{
+ uint8_t bVolNumber;
+ const char *pszOutputDir;
+ const char *pszBDevSpec;
+ #if REDCONF_API_POSIX == 1
+ const char *pszVolName;
+ #endif
+ bool fNoWarn;
+} IMGCOPYPARAM;
+
+typedef struct
+{
+ #if REDCONF_API_POSIX == 1
+ const char *pszVolume; /* Volume path prefix. */
+ uint32_t ulVolPrefixLen; /* strlen(COPIER::pszVolume) */
+ #else
+ uint8_t bVolNum; /* Volume number. */
+ #endif
+ const char *pszOutputDir; /* Output directory path. */
+ bool fNoWarn; /* If true, no warning to overwrite. */
+ uint8_t *pbCopyBuffer; /* Buffer for copying file data. */
+} COPIER;
+
+
+void ImgcopyParseParams(int argc, char *argv [], IMGCOPYPARAM *pParam);
+int ImgcopyStart(IMGCOPYPARAM *pParam);
+
+/* Implemented separately in imgcopywin.c and imgcopylinux.c. These functions
+ print an error message and abort on failure.
+*/
+void ImgcopyMkdir(const char *pszDir);
+void ImgcopyRecursiveRmdir(const char *pszDir);
+
+
+#endif /* REDTOOLS_H */
+
<!-- This macro is updated automatically: do not edit! -->
*/
-#define RED_BUILD_NUMBER "677"
+#define RED_BUILD_NUMBER "700"
#define RED_KIT_GPL 0U /* Open source GPL kit. */
#define RED_KIT_COMMERCIAL 1U /* Commercially-licensed kit. */
/** @brief Version number to display in output.
*/
-#define RED_VERSION "v1.0.4"
+#define RED_VERSION "v2.0"
+/** @brief Version number in hex.
+
+ The most significant byte is the major version number, etc.
+*/
+#define RED_VERSION_VAL 0x02000000U
/** @brief On-disk version number.
/* Specifies whether the product is in alpha stage, beta stage, or neither.
*/
#if 0
- #if 1
+ #if 0
#define ALPHABETA " (Alpha)"
#else
#define ALPHABETA " (Beta)"
/** @brief Product copyright.
*/
-#define RED_PRODUCT_LEGAL "Copyright (c) 2014-2016 Datalight, Inc. All Rights Reserved Worldwide."
+#define RED_PRODUCT_LEGAL "Copyright (c) 2014-2017 Datalight, Inc. All Rights Reserved Worldwide."
/** @brief Product patents.
#error "The STM32 StdPeriph driver is not supported. Please use the HAL driver or modify the Reliance Edge block device interface."
#endif
+
/** @brief Number of times to call BSP_SD_GetStatus() before timing out and
returning an error.
#include <redcoreapi.h>\r
#include <redvolume.h>\r
#include <redposix.h>\r
-#include "redpath.h"\r
+#include <redpath.h>\r
\r
\r
static bool IsRootDir(const char *pszLocalPath);\r
Example | Result\r
------- | ------\r
"" false\r
- "\" false\r
- "\\" false\r
+ "/" false\r
+ "//" false\r
"a" true\r
- "\a" true\r
- "\\a" true\r
+ "/a" true\r
+ "//a" true\r
\r
@param pszPathIdx The path to examine, incremented to the point of\r
interest.\r
#include <redvolume.h>\r
#include <redcoreapi.h>\r
#include <redposix.h>\r
-#include "redpath.h"\r
+#include <redpath.h>\r
\r
\r
/*-------------------------------------------------------------------\r
-/*\r
- * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.\r
- *\r
- * This program is free software; you can redistribute it and/or modify it\r
- * under the terms of version 2 of the GNU General Public License as\r
- * published by the Free Software Foundation.\r
- *\r
- * This program is distributed in the hope that it would be useful, but\r
- * WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\r
- *\r
- * Further, this software is distributed without any warranty that it is\r
- * free of the rightful claim of any third person regarding infringement\r
- * or the like. Any license provided herein, whether implied or\r
- * otherwise, applies only to this software file. Patent licenses, if\r
- * any, provided herein do not apply to combinations of this program with\r
- * other software, or any other product whatsoever.\r
- *\r
- * You should have received a copy of the GNU General Public License along\r
- * with this program; if not, write the Free Software Foundation, Inc.,\r
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
- *\r
- * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,\r
- * Mountain View, CA 94043, or:\r
- *\r
- * http://www.sgi.com\r
- *\r
- * For further information regarding this notice, see:\r
- *\r
- * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/\r
- */\r
-/** @file\r
- @brief File system stress test.\r
-\r
- This version of SGI fsstress has been modified to be single-threaded and to\r
- work with the Reliance Edge POSIX-like API.\r
-*/\r
-#include <stdio.h>\r
-#include <stdlib.h>\r
-#include <limits.h>\r
-#include <string.h>\r
-#include <stdarg.h>\r
-#include <time.h>\r
-\r
-#include <redposix.h>\r
-#include <redtests.h>\r
-\r
-#if FSSTRESS_SUPPORTED\r
-\r
-#include "redposixcompat.h"\r
-\r
-#include <redosserv.h>\r
-#include <redutils.h>\r
-#include <redmacs.h>\r
-#include <redvolume.h>\r
-#include <redgetopt.h>\r
-#include <redtoolcmn.h>\r
-\r
-#if REDCONF_CHECKER == 1\r
-#include <redcoreapi.h>\r
-#endif\r
-\r
-\r
-/* Create POSIX types. Use #define to avoid name conflicts in those\r
- environments where the type names already exist.\r
-*/\r
-#define off_t int64_t\r
-#define off64_t off_t\r
-#define ino_t uint32_t\r
-#define mode_t uint16_t\r
-#define __int64_t int64_t\r
-\r
-\r
-/** @brief Generate a random number.\r
-\r
- @return A nonnegative random number.\r
-*/\r
-#define random() ((int)(RedRand32(NULL) & 0x7FFFFFFF))\r
-\r
-\r
-/** @brief Seed the random number generator.\r
-*/\r
-#define srandom(seed) RedRandSeed(seed)\r
-\r
-\r
-#define _exit(status) exit(status)\r
-#define getpagesize() 4096U\r
-#define getpid() 1\r
-\r
-\r
-/** @brief Determine the maximum file size.\r
-\r
- This is used for the MAXFSSIZE macro.\r
-*/\r
-static uint64_t MaxFileSize(void)\r
-{\r
- REDSTATFS info;\r
- int32_t iStatus;\r
- REDSTATUS errnoSave = errno;\r
- uint64_t ullMaxFileSize;\r
-\r
- iStatus = red_statvfs("", &info);\r
- if(iStatus == 0)\r
- {\r
- ullMaxFileSize = info.f_maxfsize;\r
- }\r
- else\r
- {\r
- /* This function does not change errno.\r
- */\r
- errno = errnoSave;\r
-\r
- ullMaxFileSize = 0x7FFFFFFFU;\r
- }\r
-\r
- return ullMaxFileSize;\r
-}\r
-\r
-\r
-/*-------------------------------------------------------------------\r
- Simulated current working directory support\r
--------------------------------------------------------------------*/\r
-\r
-\r
-/* Forward declaration for red_chdir().\r
-*/\r
-static int red_stat(const char *pszPath, REDSTAT *pStat);\r
-\r
-/* The simulated CWD functions.\r
-*/\r
-#undef chdir\r
-#undef getcwd\r
-#define chdir(path) red_chdir(path)\r
-#define getcwd(buf, size) red_getcwd(buf, size)\r
-\r
-\r
-/* Redefine the path-based APIs to call MakeFullPath() on their arguments\r
- since there is no CWD support in the red_*() APIs.\r
-*/\r
-#undef open\r
-#undef unlink\r
-#undef mkdir\r
-#undef rmdir\r
-#undef rename\r
-#undef link\r
-#undef opendir\r
-#define open(path, oflag) red_open(MakeFullPath(path), oflag)\r
-#define unlink(path) red_unlink(MakeFullPath(path))\r
-#define mkdir(path) red_mkdir(MakeFullPath(path))\r
-#define rmdir(path) red_rmdir(MakeFullPath(path))\r
-#define rename(old, new) red_rename(MakeFullPath(old), MakeFullPath(new))\r
-#define link(path, hardlink) red_link(MakeFullPath(path), MakeFullPath(hardlink))\r
-#define opendir(path) red_opendir(MakeFullPath(path))\r
-\r
-\r
-/* Stores the simulated current working directory.\r
-*/\r
-static char szLocalCwd[1024U] = "/";\r
-\r
-\r
-/** @brief Change the current working directory.\r
-\r
- This function only supports a subset of what is possible with POSIX chdir().\r
-\r
- @param pszPath The new current working directory.\r
-\r
- @return Upon successful completion, 0 shall be returned. Otherwise, -1\r
- shall be returned, and errno shall be set to indicate the error.\r
-*/\r
-static int red_chdir(\r
- const char *pszPath)\r
-{\r
- uint32_t ulIdx;\r
- int iErrno = 0;\r
-\r
- if(strcmp(pszPath, "..") == 0)\r
- {\r
- uint32_t ulLastSlashIdx = 0U;\r
-\r
- /* Chop off the last path separator and everything after it, so that\r
- "/foo/bar/baz" becomes "/foo/bar", moving the CWD up one directory.\r
- */\r
- for(ulIdx = 0U; szLocalCwd[ulIdx] != '\0'; ulIdx++)\r
- {\r
- if(szLocalCwd[ulIdx] == '/')\r
- {\r
- ulLastSlashIdx = ulIdx;\r
- }\r
- }\r
-\r
- if(ulLastSlashIdx != 0U)\r
- {\r
- szLocalCwd[ulLastSlashIdx] = '\0';\r
- }\r
- }\r
- else\r
- {\r
- char szOldCwd[1024U];\r
-\r
- /* chdir() must have no effect on the CWD if it fails, so save the CWD\r
- so we can revert it if necessary.\r
- */\r
- strcpy(szOldCwd, szLocalCwd);\r
-\r
- if(pszPath[0U] == '/')\r
- {\r
- if(strlen(pszPath) >= sizeof(szLocalCwd))\r
- {\r
- iErrno = RED_ENAMETOOLONG;\r
- }\r
- else\r
- {\r
- strcpy(szLocalCwd, pszPath);\r
- }\r
- }\r
- else\r
- {\r
- ulIdx = strlen(szLocalCwd);\r
-\r
- if((ulIdx + 1U + strlen(pszPath)) >= sizeof(szLocalCwd))\r
- {\r
- iErrno = RED_ENAMETOOLONG;\r
- }\r
- else\r
- {\r
- if(szLocalCwd[1U] != '\0')\r
- {\r
- szLocalCwd[ulIdx] = '/';\r
- ulIdx++;\r
- }\r
-\r
- strcpy(&szLocalCwd[ulIdx], pszPath);\r
- }\r
- }\r
-\r
- if(iErrno == 0)\r
- {\r
- REDSTAT s;\r
- int iStatus;\r
-\r
- iStatus = red_stat(szLocalCwd, &s);\r
- if(iStatus != 0)\r
- {\r
- iErrno = errno;\r
- }\r
- else if(!S_ISDIR(s.st_mode))\r
- {\r
- iErrno = RED_ENOTDIR;\r
- }\r
- else\r
- {\r
- /* No error, new CWD checks out.\r
- */\r
- }\r
- }\r
-\r
- if(iErrno != 0)\r
- {\r
- strcpy(szLocalCwd, szOldCwd);\r
- }\r
- }\r
-\r
- if(iErrno != 0)\r
- {\r
- errno = iErrno;\r
- }\r
-\r
- return iErrno == 0 ? 0 : -1;\r
-}\r
-\r
-\r
-/** @brief Retrieve the current working directory.\r
-\r
- @param pszBuf On successful return, populated with the current working\r
- directory. If NULL, memory will be allocated for the CWD\r
- and returned by this function.\r
- @param nSize The size of @p pszBuf.\r
-\r
- @return On success, if @p pszBuf was non-NULL, returns @p pszBuf; if\r
- @p pszBuf was NULL, returns an allocated buffer populated with the\r
- CWD which must be freed by the caller. On failure, returns NULL\r
- and errno will be set.\r
-*/\r
-static char *red_getcwd(\r
- char *pszBuf,\r
- size_t nSize)\r
-{\r
- char *pszRet;\r
-\r
- if(pszBuf == NULL)\r
- {\r
- pszRet = malloc(strlen(szLocalCwd) + 1U);\r
- if(pszRet == NULL)\r
- {\r
- errno = RED_ENOMEM;\r
- }\r
- else\r
- {\r
- strcpy(pszRet, szLocalCwd);\r
- }\r
- }\r
- else if(nSize < strlen(szLocalCwd) + 1U)\r
- {\r
- errno = RED_ERANGE;\r
- pszRet = NULL;\r
- }\r
- else\r
- {\r
- strcpy(pszBuf, szLocalCwd);\r
- pszRet = pszBuf;\r
- }\r
-\r
- return pszRet;\r
-}\r
-\r
-\r
-/** @brief Make a relative path into a fully qualified path.\r
-\r
- @param pszName The relative path.\r
-\r
- @return On success, a pointer to a fully qualified path. On error, NULL.\r
-*/\r
-static const char *MakeFullPath(\r
- const char *pszName)\r
-{\r
- #define MAXVOLNAME 64U /* Enough for most configs. */\r
- static char aszFullPath[2U][MAXVOLNAME + 1U + 1024U];\r
- static uint32_t ulWhich = 0U;\r
-\r
- char *pszFullPath = aszFullPath[ulWhich];\r
- const char *pszVolume = gpRedVolConf->pszPathPrefix;\r
- int32_t iLen;\r
-\r
- if(pszName[0U] == '/')\r
- {\r
- iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s%s", pszVolume, pszName);\r
- }\r
- else if(strcmp(pszName, ".") == 0U)\r
- {\r
- iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s%s", pszVolume, szLocalCwd);\r
- }\r
- else if((szLocalCwd[0U] == '/') && (szLocalCwd[1U] == '\0'))\r
- {\r
- iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s/%s", pszVolume, pszName);\r
- }\r
- else\r
- {\r
- iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s%s/%s", pszVolume, szLocalCwd, pszName);\r
- }\r
-\r
- if(iLen == -1)\r
- {\r
- /* Insufficient path buffer space.\r
- */\r
- pszFullPath = NULL;\r
- }\r
- else\r
- {\r
- /* Toggle between two full path arrays; a kluge to make rename() and\r
- link() work correctly.\r
- */\r
- ulWhich ^= 1U;\r
- }\r
-\r
- return pszFullPath;\r
-}\r
-\r
-\r
-/*-------------------------------------------------------------------\r
- POSIX functions not implemented by the RED POSIX-like API\r
--------------------------------------------------------------------*/\r
-\r
-#define stat(p, s) red_stat(p, s)\r
-#define stat64(p, s) stat(p, s)\r
-#define lstat(p, s) stat(p, s)\r
-#define lstat64(p, s) stat(p, s)\r
-#define truncate(p, s) red_truncate(p, s)\r
-#define truncate64(p, s) truncate(p, s)\r
-\r
-\r
-/** @brief Get the status of a file or directory.\r
-*/\r
-static int red_stat(\r
- const char *pszPath,\r
- REDSTAT *pStat)\r
-{\r
- int iFd;\r
- int iRet;\r
-\r
- iFd = open(pszPath, O_RDONLY);\r
- iRet = iFd;\r
- if(iFd != -1)\r
- {\r
- iRet = fstat(iFd, pStat);\r
-\r
- (void)close(iFd);\r
- }\r
-\r
- return iRet;\r
-}\r
-\r
-\r
-/** @brief Truncate a file to a specified length.\r
-*/\r
-static int red_truncate(\r
- const char *pszPath,\r
- off_t llSize)\r
-{\r
- int iFd;\r
- int iRet;\r
-\r
- iFd = open(pszPath, O_WRONLY);\r
- iRet = iFd;\r
- if(iFd != -1)\r
- {\r
- iRet = ftruncate(iFd, llSize);\r
-\r
- (void)close(iFd);\r
- }\r
-\r
- return iRet;\r
-}\r
-\r
-\r
-/*-------------------------------------------------------------------\r
- Begin ported fsstress code\r
--------------------------------------------------------------------*/\r
-\r
-/* Stuff from xfscompat.h */\r
-\r
-#define MAXNAMELEN (REDCONF_NAME_MAX+1U) /* Assumed to include NUL */\r
-\r
-struct dioattr {\r
- int d_miniosz, d_maxiosz, d_mem;\r
-};\r
-\r
-#define MIN(a,b) ((a)<(b) ? (a):(b))\r
-#define MAX(a,b) ((a)>(b) ? (a):(b))\r
-\r
-/* End xfscompat.h */\r
-\r
-\r
-typedef enum {\r
- OP_CREAT,\r
- OP_FDATASYNC,\r
- OP_FSYNC,\r
- OP_GETDENTS,\r
- OP_LINK,\r
- OP_MKDIR,\r
- OP_READ,\r
- OP_RENAME,\r
- OP_RMDIR,\r
- OP_STAT,\r
- OP_TRUNCATE,\r
- OP_UNLINK,\r
- OP_WRITE,\r
- #if REDCONF_CHECKER == 1\r
- OP_CHECK,\r
- #endif\r
- OP_LAST\r
-} opty_t;\r
-\r
-typedef void (*opfnc_t) (int, long);\r
-\r
-typedef struct opdesc {\r
- opty_t op;\r
- const char *name;\r
- opfnc_t func;\r
- int freq;\r
- int iswrite;\r
-} opdesc_t;\r
-\r
-typedef struct fent {\r
- int id;\r
- int parent;\r
-} fent_t;\r
-\r
-typedef struct flist {\r
- int nfiles;\r
- int nslots;\r
- int tag;\r
- fent_t *fents;\r
-} flist_t;\r
-\r
-typedef struct pathname {\r
- int len;\r
- char *path;\r
-} pathname_t;\r
-\r
-#define FT_DIR 0\r
-#define FT_DIRm (1 << FT_DIR)\r
-#define FT_REG 1\r
-#define FT_REGm (1 << FT_REG)\r
-#define FT_SYM 2\r
-#define FT_SYMm (1 << FT_SYM)\r
-#define FT_DEV 3\r
-#define FT_DEVm (1 << FT_DEV)\r
-#define FT_RTF 4\r
-#define FT_RTFm (1 << FT_RTF)\r
-#define FT_nft 5\r
-#define FT_ANYm ((1 << FT_nft) - 1)\r
-#define FT_REGFILE (FT_REGm | FT_RTFm)\r
-#define FT_NOTDIR (FT_ANYm & ~FT_DIRm)\r
-\r
-#define FLIST_SLOT_INCR 16\r
-#define NDCACHE 64\r
-\r
-#define MAXFSIZE MaxFileSize()\r
-\r
-static void creat_f(int opno, long r);\r
-static void fdatasync_f(int opno, long r);\r
-static void fsync_f(int opno, long r);\r
-static void getdents_f(int opno, long r);\r
-static void link_f(int opno, long r);\r
-static void mkdir_f(int opno, long r);\r
-static void read_f(int opno, long r);\r
-static void rename_f(int opno, long r);\r
-static void rmdir_f(int opno, long r);\r
-static void stat_f(int opno, long r);\r
-static void truncate_f(int opno, long r);\r
-static void unlink_f(int opno, long r);\r
-static void write_f(int opno, long r);\r
-#if REDCONF_CHECKER == 1\r
-static void check_f(int opno, long r);\r
-#endif\r
-\r
-static opdesc_t ops[] = {\r
- {OP_CREAT, "creat", creat_f, 4, 1},\r
- {OP_FDATASYNC, "fdatasync", fdatasync_f, 1, 1},\r
- {OP_FSYNC, "fsync", fsync_f, 1, 1},\r
- {OP_GETDENTS, "getdents", getdents_f, 1, 0},\r
- {OP_LINK, "link", link_f, 1, 1},\r
- {OP_MKDIR, "mkdir", mkdir_f, 2, 1},\r
- {OP_READ, "read", read_f, 1, 0},\r
- {OP_RENAME, "rename", rename_f, 2, 1},\r
- {OP_RMDIR, "rmdir", rmdir_f, 1, 1},\r
- {OP_STAT, "stat", stat_f, 1, 0},\r
- {OP_TRUNCATE, "truncate", truncate_f, 2, 1},\r
- {OP_UNLINK, "unlink", unlink_f, 1, 1},\r
- {OP_WRITE, "write", write_f, 4, 1},\r
- #if REDCONF_CHECKER == 1\r
- {OP_CHECK, "check", check_f, 1, 1},\r
- #endif\r
-}, *ops_end;\r
-\r
-static flist_t flist[FT_nft] = {\r
- {0, 0, 'd', NULL},\r
- {0, 0, 'f', NULL},\r
- {0, 0, 'l', NULL},\r
- {0, 0, 'c', NULL},\r
- {0, 0, 'r', NULL},\r
-};\r
-\r
-static int dcache[NDCACHE];\r
-static opty_t *freq_table;\r
-static int freq_table_size;\r
-static char *homedir;\r
-static int *ilist;\r
-static int ilistlen;\r
-static off64_t maxfsize;\r
-static int namerand;\r
-static int nameseq;\r
-static int nops;\r
-static int operations = 1;\r
-static int procid;\r
-static int rtpct;\r
-static unsigned long seed = 0;\r
-static ino_t top_ino;\r
-static int verbose = 0;\r
-\r
-static int delete_tree(const char *path);\r
-static void add_to_flist(int fd, int it, int parent);\r
-static void append_pathname(pathname_t *name, const char *str);\r
-static void check_cwd(void);\r
-static int creat_path(pathname_t *name, mode_t mode);\r
-static void dcache_enter(int dirid, int slot);\r
-static void dcache_init(void);\r
-static fent_t *dcache_lookup(int dirid);\r
-static void dcache_purge(int dirid);\r
-static void del_from_flist(int ft, int slot);\r
-static void doproc(void);\r
-static void fent_to_name(pathname_t *name, flist_t *flp, fent_t *fep);\r
-static void fix_parent(int oldid, int newid);\r
-static void free_pathname(pathname_t *name);\r
-static int generate_fname(fent_t *fep, int ft, pathname_t *name, int *idp, int *v);\r
-static int get_fname(int which, long r, pathname_t *name, flist_t **flpp, fent_t **fepp, int *v);\r
-static void init_pathname(pathname_t *name);\r
-static int link_path(pathname_t *name1, pathname_t *name2);\r
-static int lstat64_path(pathname_t *name, REDSTAT *sbuf);\r
-static void make_freq_table(void);\r
-static int mkdir_path(pathname_t *name, mode_t mode);\r
-static void namerandpad(int id, char *buf, int len);\r
-static int open_path(pathname_t *name, int oflag);\r
-static DIR *opendir_path(pathname_t *name);\r
-static int rename_path(pathname_t *name1, pathname_t *name2);\r
-static int rmdir_path(pathname_t *name);\r
-static void separate_pathname(pathname_t *name, char *buf, pathname_t *newname);\r
-static int stat64_path(pathname_t *name, REDSTAT *sbuf);\r
-static int truncate64_path(pathname_t *name, off64_t length);\r
-static int unlink_path(pathname_t *name);\r
-static void usage(const char *progname);\r
-\r
-\r
-/** @brief Parse parameters for fsstress.\r
-\r
- @param argc The number of arguments from main().\r
- @param argv The vector of arguments from main().\r
- @param pParam Populated with the fsstress parameters.\r
- @param pbVolNum If non-NULL, populated with the volume number.\r
- @param ppszDevice If non-NULL, populated with the device name argument or\r
- NULL if no device argument is provided.\r
-\r
- @return The result of parsing the parameters.\r
-*/\r
-PARAMSTATUS FsstressParseParams(\r
- int argc,\r
- char *argv[],\r
- FSSTRESSPARAM *pParam,\r
- uint8_t *pbVolNum,\r
- const char **ppszDevice)\r
-{\r
- int c;\r
- uint8_t bVolNum;\r
- const REDOPTION aLongopts[] =\r
- {\r
- { "no-cleanup", red_no_argument, NULL, 'c' },\r
- { "loops", red_required_argument, NULL, 'l' },\r
- { "nops", red_required_argument, NULL, 'n' },\r
- { "namepad", red_no_argument, NULL, 'r' },\r
- { "seed", red_required_argument, NULL, 's' },\r
- { "verbose", red_no_argument, NULL, 'v' },\r
- { "dev", red_required_argument, NULL, 'D' },\r
- { "help", red_no_argument, NULL, 'H' },\r
- { NULL }\r
- };\r
-\r
- /* If run without parameters, treat as a help request.\r
- */\r
- if(argc <= 1)\r
- {\r
- goto Help;\r
- }\r
-\r
- /* Assume no device argument to start with.\r
- */\r
- if(ppszDevice != NULL)\r
- {\r
- *ppszDevice = NULL;\r
- }\r
-\r
- /* Set default parameters.\r
- */\r
- FsstressDefaultParams(pParam);\r
-\r
- while((c = RedGetoptLong(argc, argv, "cl:n:rs:vD:H", aLongopts, NULL)) != -1)\r
- {\r
- switch(c)\r
- {\r
- case 'c': /* --no-cleanup */\r
- pParam->fNoCleanup = true;\r
- break;\r
- case 'l': /* --loops */\r
- pParam->ulLoops = RedAtoI(red_optarg);\r
- break;\r
- case 'n': /* --nops */\r
- pParam->ulNops = RedAtoI(red_optarg);\r
- break;\r
- case 'r': /* --namepad */\r
- pParam->fNamePad = true;\r
- break;\r
- case 's': /* --seed */\r
- pParam->ulSeed = RedAtoI(red_optarg);\r
- break;\r
- case 'v': /* --verbose */\r
- pParam->fVerbose = true;\r
- break;\r
- case 'D': /* --dev */\r
- if(ppszDevice != NULL)\r
- {\r
- *ppszDevice = red_optarg;\r
- }\r
- break;\r
- case 'H': /* --help */\r
- goto Help;\r
- case '?': /* Unknown or ambiguous option */\r
- case ':': /* Option missing required argument */\r
- default:\r
- goto BadOpt;\r
- }\r
- }\r
-\r
- /* RedGetoptLong() has permuted argv to move all non-option arguments to\r
- the end. We expect to find a volume identifier.\r
- */\r
- if(red_optind >= argc)\r
- {\r
- RedPrintf("Missing volume argument\n");\r
- goto BadOpt;\r
- }\r
-\r
- bVolNum = RedFindVolumeNumber(argv[red_optind]);\r
- if(bVolNum == REDCONF_VOLUME_COUNT)\r
- {\r
- RedPrintf("Error: \"%s\" is not a valid volume identifier.\n", argv[red_optind]);\r
- goto BadOpt;\r
- }\r
-\r
- if(pbVolNum != NULL)\r
- {\r
- *pbVolNum = bVolNum;\r
- }\r
-\r
- red_optind++; /* Move past volume parameter. */\r
- if(red_optind < argc)\r
- {\r
- int32_t ii;\r
-\r
- for(ii = red_optind; ii < argc; ii++)\r
- {\r
- RedPrintf("Error: Unexpected command-line argument \"%s\".\n", argv[ii]);\r
- }\r
-\r
- goto BadOpt;\r
- }\r
-\r
- return PARAMSTATUS_OK;\r
-\r
- BadOpt:\r
-\r
- RedPrintf("%s - invalid parameters\n", argv[0U]);\r
- usage(argv[0U]);\r
- return PARAMSTATUS_BAD;\r
-\r
- Help:\r
-\r
- usage(argv[0U]);\r
- return PARAMSTATUS_HELP;\r
-}\r
-\r
-\r
-/** @brief Set default fsstress parameters.\r
-\r
- @param pParam Populated with the default fsstress parameters.\r
-*/\r
-void FsstressDefaultParams(\r
- FSSTRESSPARAM *pParam)\r
-{\r
- RedMemSet(pParam, 0U, sizeof(*pParam));\r
- pParam->ulLoops = 1U;\r
- pParam->ulNops = 10000U;\r
-}\r
-\r
-\r
-/** @brief Start fsstress.\r
-\r
- @param pParam fsstress parameters, either from FsstressParseParams() or\r
- constructed programatically.\r
-\r
- @return Zero on success, otherwise nonzero.\r
-*/\r
-int FsstressStart(\r
- const FSSTRESSPARAM *pParam)\r
-{\r
- char buf[10];\r
- int fd;\r
- int i;\r
- int cleanup;\r
- int loops;\r
- int loopcntr = 1;\r
-\r
- nops = sizeof(ops) / sizeof(ops[0]);\r
- ops_end = &ops[nops];\r
-\r
- /* Copy the already-parsed parameters into the traditional variables.\r
- */\r
- cleanup = pParam->fNoCleanup ? 1 : 0;\r
- loops = pParam->ulLoops;\r
- operations = pParam->ulNops;\r
- namerand = pParam->fNamePad ? 1 : 0;\r
- seed = pParam->ulSeed;\r
- verbose = pParam->fVerbose ? 1 : 0;\r
-\r
- make_freq_table();\r
-\r
- while ((loopcntr <= loops) || (loops == 0)) {\r
- RedSNPrintf(buf, sizeof(buf), "fss%x", getpid());\r
- fd = creat(buf, 0666);\r
- maxfsize = (off64_t) MAXFSIZE;\r
- dcache_init();\r
- if (!seed) {\r
- seed = (unsigned long)RedOsClockGetTime();\r
- RedPrintf("seed = %ld\n", seed);\r
- }\r
- close(fd);\r
- unlink(buf);\r
- procid = 0;\r
- doproc();\r
- if (cleanup == 0) {\r
- delete_tree("/");\r
- for (i = 0; i < FT_nft; i++) {\r
- flist[i].nslots = 0;\r
- flist[i].nfiles = 0;\r
- free(flist[i].fents);\r
- flist[i].fents = NULL;\r
- }\r
- }\r
- loopcntr++;\r
- }\r
- return 0;\r
-}\r
-\r
-static int delete_tree(const char *path)\r
-{\r
- REDSTAT sb;\r
- DIR *dp;\r
- REDDIRENT *dep;\r
- char *childpath;\r
- size_t len;\r
- int e;\r
-\r
- e = stat(path, &sb);\r
- if (e)\r
- return errno;\r
-\r
- if (!S_ISDIR(sb.st_mode))\r
- return unlink(path) ? errno : 0;\r
-\r
- dp = opendir(path);\r
- if (dp == NULL)\r
- return errno;\r
-\r
- while((dep = readdir(dp)) != NULL) {\r
- len = strlen(path) + 1 + strlen(dep->d_name) + 1;\r
- childpath = malloc(len);\r
-\r
- strcpy(childpath, path);\r
- if (childpath[strlen(childpath) - 1] != '/')\r
- strcat(childpath, "/");\r
- strcat(childpath, dep->d_name);\r
-\r
- e = delete_tree(childpath);\r
-\r
- free(childpath);\r
-\r
- if (e)\r
- break;\r
- }\r
-\r
- if (e == 0 && strcmp(path, "/") != 0) {\r
- e = rmdir(path) ? errno : 0;\r
- }\r
- closedir(dp);\r
- return e;\r
-}\r
-\r
-static void add_to_flist(int ft, int id, int parent)\r
-{\r
- fent_t *fep;\r
- flist_t *ftp;\r
-\r
- ftp = &flist[ft];\r
- if (ftp->nfiles == ftp->nslots) {\r
- ftp->nslots += FLIST_SLOT_INCR;\r
- ftp->fents = realloc(ftp->fents, ftp->nslots * sizeof(fent_t));\r
- }\r
- fep = &ftp->fents[ftp->nfiles++];\r
- fep->id = id;\r
- fep->parent = parent;\r
-}\r
-\r
-static void append_pathname(pathname_t *name, const char *str)\r
-{\r
- int len;\r
-\r
- len = strlen(str);\r
-#ifdef DEBUG\r
- if (len && *str == '/' && name->len == 0) {\r
- RedPrintf("fsstress: append_pathname failure\n");\r
- chdir(homedir);\r
- abort();\r
-\r
- }\r
-#endif\r
- name->path = realloc(name->path, name->len + 1 + len);\r
- strcpy(&name->path[name->len], str);\r
- name->len += len;\r
-}\r
-\r
-static void check_cwd(void)\r
-{\r
-#ifdef DEBUG\r
- REDSTAT statbuf;\r
-\r
- if (stat64(".", &statbuf) == 0 && statbuf.st_ino == top_ino)\r
- return;\r
- chdir(homedir);\r
- RedPrintf("fsstress: check_cwd failure\n");\r
- abort();\r
-\r
-#endif\r
-}\r
-\r
-static int creat_path(pathname_t *name, mode_t mode)\r
-{\r
- char buf[MAXNAMELEN];\r
- pathname_t newname;\r
- int rval;\r
-\r
- rval = creat(name->path, mode);\r
- if (rval >= 0 || errno != RED_ENAMETOOLONG)\r
- return rval;\r
- separate_pathname(name, buf, &newname);\r
- if (chdir(buf) == 0) {\r
- rval = creat_path(&newname, mode);\r
- chdir("..");\r
- }\r
- free_pathname(&newname);\r
- return rval;\r
-}\r
-\r
-static void dcache_enter(int dirid, int slot)\r
-{\r
- dcache[dirid % NDCACHE] = slot;\r
-}\r
-\r
-static void dcache_init(void)\r
-{\r
- int i;\r
-\r
- for (i = 0; i < NDCACHE; i++)\r
- dcache[i] = -1;\r
-}\r
-\r
-static fent_t *dcache_lookup(int dirid)\r
-{\r
- fent_t *fep;\r
- int i;\r
-\r
- i = dcache[dirid % NDCACHE];\r
- if (i >= 0 && (fep = &flist[FT_DIR].fents[i])->id == dirid)\r
- return fep;\r
- return NULL;\r
-}\r
-\r
-static void dcache_purge(int dirid)\r
-{\r
- int *dcp;\r
-\r
- dcp = &dcache[dirid % NDCACHE];\r
- if (*dcp >= 0 && flist[FT_DIR].fents[*dcp].id == dirid)\r
- *dcp = -1;\r
-}\r
-\r
-static void del_from_flist(int ft, int slot)\r
-{\r
- flist_t *ftp;\r
-\r
- ftp = &flist[ft];\r
- if (ft == FT_DIR)\r
- dcache_purge(ftp->fents[slot].id);\r
- if (slot != ftp->nfiles - 1) {\r
- if (ft == FT_DIR)\r
- dcache_purge(ftp->fents[ftp->nfiles - 1].id);\r
- ftp->fents[slot] = ftp->fents[--ftp->nfiles];\r
- } else\r
- ftp->nfiles--;\r
-}\r
-\r
-static fent_t *dirid_to_fent(int dirid)\r
-{\r
- fent_t *efep;\r
- fent_t *fep;\r
- flist_t *flp;\r
-\r
- if ((fep = dcache_lookup(dirid)))\r
- return fep;\r
- flp = &flist[FT_DIR];\r
- for (fep = flp->fents, efep = &fep[flp->nfiles]; fep < efep; fep++) {\r
- if (fep->id == dirid) {\r
- dcache_enter(dirid, (int)(fep - flp->fents));\r
- return fep;\r
- }\r
- }\r
- return NULL;\r
-}\r
-\r
-static void doproc(void)\r
-{\r
- REDSTAT statbuf;\r
- char buf[10];\r
- int opno;\r
- opdesc_t *p;\r
-\r
- RedSNPrintf(buf, sizeof(buf), "p%x", procid);\r
- (void)mkdir(buf);\r
- if (chdir(buf) < 0 || stat64(".", &statbuf) < 0) {\r
- perror(buf);\r
- _exit(1);\r
- }\r
- top_ino = statbuf.st_ino;\r
- homedir = getcwd(NULL, 0);\r
- seed += procid;\r
- srandom(seed);\r
- if (namerand)\r
- namerand = random();\r
- for (opno = 0; opno < operations; opno++) {\r
- p = &ops[freq_table[random() % freq_table_size]];\r
- if ((unsigned long)p->func < 4096)\r
- abort();\r
-\r
- p->func(opno, random());\r
- }\r
- free(homedir);\r
-}\r
-\r
-static void fent_to_name(pathname_t *name, flist_t *flp, fent_t *fep)\r
-{\r
- char buf[MAXNAMELEN];\r
- int i;\r
- fent_t *pfep;\r
-\r
- if (fep == NULL)\r
- return;\r
- if (fep->parent != -1) {\r
- pfep = dirid_to_fent(fep->parent);\r
- fent_to_name(name, &flist[FT_DIR], pfep);\r
- append_pathname(name, "/");\r
- }\r
- i = RedSNPrintf(buf, sizeof(buf), "%c%x", flp->tag, fep->id);\r
- namerandpad(fep->id, buf, i);\r
- append_pathname(name, buf);\r
-}\r
-\r
-static void fix_parent(int oldid, int newid)\r
-{\r
- fent_t *fep;\r
- flist_t *flp;\r
- int i;\r
- int j;\r
-\r
- for (i = 0, flp = flist; i < FT_nft; i++, flp++) {\r
- for (j = 0, fep = flp->fents; j < flp->nfiles; j++, fep++) {\r
- if (fep->parent == oldid)\r
- fep->parent = newid;\r
- }\r
- }\r
-}\r
-\r
-static void free_pathname(pathname_t *name)\r
-{\r
- if (name->path) {\r
- free(name->path);\r
- name->path = NULL;\r
- name->len = 0;\r
- }\r
-}\r
-\r
-static int generate_fname(fent_t *fep, int ft, pathname_t *name, int *idp, int *v)\r
-{\r
- char buf[MAXNAMELEN];\r
- flist_t *flp;\r
- int id;\r
- int j;\r
- int len;\r
-\r
- flp = &flist[ft];\r
- len = RedSNPrintf(buf, sizeof(buf), "%c%x", flp->tag, id = nameseq++);\r
- namerandpad(id, buf, len);\r
- if (fep) {\r
- fent_to_name(name, &flist[FT_DIR], fep);\r
- append_pathname(name, "/");\r
- }\r
- append_pathname(name, buf);\r
- *idp = id;\r
- *v = verbose;\r
- for (j = 0; !*v && j < ilistlen; j++) {\r
- if (ilist[j] == id) {\r
- *v = 1;\r
- break;\r
- }\r
- }\r
- return 1;\r
-}\r
-\r
-static int\r
-get_fname(int which, long r, pathname_t *name, flist_t **flpp, fent_t **fepp, int *v)\r
-{\r
- int c;\r
- fent_t *fep;\r
- flist_t *flp;\r
- int i;\r
- int j;\r
- int x;\r
-\r
- for (i = 0, c = 0, flp = flist; i < FT_nft; i++, flp++) {\r
- if (which & (1 << i))\r
- c += flp->nfiles;\r
- }\r
- if (c == 0) {\r
- if (flpp)\r
- *flpp = NULL;\r
- if (fepp)\r
- *fepp = NULL;\r
- *v = verbose;\r
- return 0;\r
- }\r
- x = (int)(r % c);\r
- for (i = 0, c = 0, flp = flist; i < FT_nft; i++, flp++) {\r
- if (which & (1 << i)) {\r
- if (x < c + flp->nfiles) {\r
- fep = &flp->fents[x - c];\r
- if (name)\r
- fent_to_name(name, flp, fep);\r
- if (flpp)\r
- *flpp = flp;\r
- if (fepp)\r
- *fepp = fep;\r
- *v = verbose;\r
- for (j = 0; !*v && j < ilistlen; j++) {\r
- if (ilist[j] == fep->id) {\r
- *v = 1;\r
- break;\r
- }\r
- }\r
- return 1;\r
- }\r
- c += flp->nfiles;\r
- }\r
- }\r
-#ifdef DEBUG\r
- RedPrintf("fsstress: get_fname failure\n");\r
- abort();\r
-#endif\r
- return -1;\r
-\r
-}\r
-\r
-static void init_pathname(pathname_t *name)\r
-{\r
- name->len = 0;\r
- name->path = NULL;\r
-}\r
-\r
-static int link_path(pathname_t *name1, pathname_t *name2)\r
-{\r
- char buf1[MAXNAMELEN];\r
- char buf2[MAXNAMELEN];\r
- int down1;\r
- pathname_t newname1;\r
- pathname_t newname2;\r
- int rval;\r
-\r
- rval = link(name1->path, name2->path);\r
- if (rval >= 0 || errno != RED_ENAMETOOLONG)\r
- return rval;\r
- separate_pathname(name1, buf1, &newname1);\r
- separate_pathname(name2, buf2, &newname2);\r
- if (strcmp(buf1, buf2) == 0) {\r
- if (chdir(buf1) == 0) {\r
- rval = link_path(&newname1, &newname2);\r
- chdir("..");\r
- }\r
- } else {\r
- if (strcmp(buf1, "..") == 0)\r
- down1 = 0;\r
- else if (strcmp(buf2, "..") == 0)\r
- down1 = 1;\r
- else if (strlen(buf1) == 0)\r
- down1 = 0;\r
- else if (strlen(buf2) == 0)\r
- down1 = 1;\r
- else\r
- down1 = MAX(newname1.len, 3 + name2->len) <=\r
- MAX(3 + name1->len, newname2.len);\r
- if (down1) {\r
- free_pathname(&newname2);\r
- append_pathname(&newname2, "../");\r
- append_pathname(&newname2, name2->path);\r
- if (chdir(buf1) == 0) {\r
- rval = link_path(&newname1, &newname2);\r
- chdir("..");\r
- }\r
- } else {\r
- free_pathname(&newname1);\r
- append_pathname(&newname1, "../");\r
- append_pathname(&newname1, name1->path);\r
- if (chdir(buf2) == 0) {\r
- rval = link_path(&newname1, &newname2);\r
- chdir("..");\r
- }\r
- }\r
- }\r
- free_pathname(&newname1);\r
- free_pathname(&newname2);\r
- return rval;\r
-}\r
-\r
-static int lstat64_path(pathname_t *name, REDSTAT *sbuf)\r
-{\r
- char buf[MAXNAMELEN];\r
- pathname_t newname;\r
- int rval;\r
-\r
- rval = lstat64(name->path, sbuf);\r
- if (rval >= 0 || errno != RED_ENAMETOOLONG)\r
- return rval;\r
- separate_pathname(name, buf, &newname);\r
- if (chdir(buf) == 0) {\r
- rval = lstat64_path(&newname, sbuf);\r
- chdir("..");\r
- }\r
- free_pathname(&newname);\r
- return rval;\r
-}\r
-\r
-static void make_freq_table(void)\r
-{\r
- int f;\r
- int i;\r
- opdesc_t *p;\r
-\r
- for (p = ops, f = 0; p < ops_end; p++)\r
- f += p->freq;\r
- freq_table = malloc(f * sizeof(*freq_table));\r
- freq_table_size = f;\r
- for (p = ops, i = 0; p < ops_end; p++) {\r
- for (f = 0; f < p->freq; f++, i++)\r
- freq_table[i] = p->op;\r
- }\r
-}\r
-\r
-static int mkdir_path(pathname_t *name, mode_t mode)\r
-{\r
- char buf[MAXNAMELEN];\r
- pathname_t newname;\r
- int rval;\r
-\r
- rval = mkdir(name->path);\r
- if (rval >= 0 || errno != RED_ENAMETOOLONG)\r
- return rval;\r
- separate_pathname(name, buf, &newname);\r
- if (chdir(buf) == 0) {\r
- rval = mkdir_path(&newname, mode);\r
- chdir("..");\r
- }\r
- free_pathname(&newname);\r
- return rval;\r
-}\r
-\r
-static void namerandpad(int id, char *buf, int len)\r
-{\r
- int bucket;\r
- static int buckets[8] = {0};\r
- static int bucket_count = 0;\r
- int bucket_value;\r
- int i;\r
- int padlen;\r
- int padmod;\r
-\r
- if (namerand == 0)\r
- return;\r
-\r
- /* buckets[] used to be a statically initialized array with the following\r
- initializer: { 2, 4, 8, 16, 32, 64, 128, MAXNAMELEN - 1 }\r
-\r
- The problem is that with Reliance Edge, the maximum name length might be\r
- less than 128. So the below code populates buckets[] in a similar\r
- fashion but avoids name lengths longer than the maximum. For example,\r
- if the max name is 20, the resulting array is { 2, 4, 8, 16, 20 }.\r
- */\r
- if (!bucket_count) {\r
- bucket_count = sizeof(buckets) / sizeof(buckets[0]);\r
- bucket_value = 2;\r
- for (i = 0; i < bucket_count; i++) {\r
- if (bucket_value > 128 || bucket_value >= (int)MAXNAMELEN - 1)\r
- break;\r
- buckets[i] = bucket_value;\r
- bucket_value *= 2;\r
- }\r
- if (i < bucket_count) {\r
- buckets[i] = MAXNAMELEN - 1;\r
- i++;\r
- }\r
- bucket_count = i;\r
- }\r
-\r
- bucket = (id ^ namerand) % bucket_count;\r
- padmod = buckets[bucket] + 1 - len;\r
- if (padmod <= 0)\r
- return;\r
- padlen = (id ^ namerand) % padmod;\r
- if (padlen) {\r
- memset(&buf[len], 'X', padlen);\r
- buf[len + padlen] = '\0';\r
- }\r
-}\r
-\r
-static int open_path(pathname_t *name, int oflag)\r
-{\r
- char buf[MAXNAMELEN];\r
- pathname_t newname;\r
- int rval;\r
-\r
- rval = open(name->path, oflag);\r
- if (rval >= 0 || errno != RED_ENAMETOOLONG)\r
- return rval;\r
- separate_pathname(name, buf, &newname);\r
- if (chdir(buf) == 0) {\r
- rval = open_path(&newname, oflag);\r
- chdir("..");\r
- }\r
- free_pathname(&newname);\r
- return rval;\r
-}\r
-\r
-static DIR *opendir_path(pathname_t *name)\r
-{\r
- char buf[MAXNAMELEN];\r
- pathname_t newname;\r
- DIR *rval;\r
-\r
- rval = opendir(name->path);\r
- if (rval || errno != RED_ENAMETOOLONG)\r
- return rval;\r
- separate_pathname(name, buf, &newname);\r
- if (chdir(buf) == 0) {\r
- rval = opendir_path(&newname);\r
- chdir("..");\r
- }\r
- free_pathname(&newname);\r
- return rval;\r
-}\r
-\r
-static int rename_path(pathname_t *name1, pathname_t *name2)\r
-{\r
- char buf1[MAXNAMELEN];\r
- char buf2[MAXNAMELEN];\r
- int down1;\r
- pathname_t newname1;\r
- pathname_t newname2;\r
- int rval;\r
-\r
- rval = rename(name1->path, name2->path);\r
- if (rval >= 0 || errno != RED_ENAMETOOLONG)\r
- return rval;\r
- separate_pathname(name1, buf1, &newname1);\r
- separate_pathname(name2, buf2, &newname2);\r
- if (strcmp(buf1, buf2) == 0) {\r
- if (chdir(buf1) == 0) {\r
- rval = rename_path(&newname1, &newname2);\r
- chdir("..");\r
- }\r
- } else {\r
- if (strcmp(buf1, "..") == 0)\r
- down1 = 0;\r
- else if (strcmp(buf2, "..") == 0)\r
- down1 = 1;\r
- else if (strlen(buf1) == 0)\r
- down1 = 0;\r
- else if (strlen(buf2) == 0)\r
- down1 = 1;\r
- else\r
- down1 = MAX(newname1.len, 3 + name2->len) <=\r
- MAX(3 + name1->len, newname2.len);\r
- if (down1) {\r
- free_pathname(&newname2);\r
- append_pathname(&newname2, "../");\r
- append_pathname(&newname2, name2->path);\r
- if (chdir(buf1) == 0) {\r
- rval = rename_path(&newname1, &newname2);\r
- chdir("..");\r
- }\r
- } else {\r
- free_pathname(&newname1);\r
- append_pathname(&newname1, "../");\r
- append_pathname(&newname1, name1->path);\r
- if (chdir(buf2) == 0) {\r
- rval = rename_path(&newname1, &newname2);\r
- chdir("..");\r
- }\r
- }\r
- }\r
- free_pathname(&newname1);\r
- free_pathname(&newname2);\r
- return rval;\r
-}\r
-\r
-static int rmdir_path(pathname_t *name)\r
-{\r
- char buf[MAXNAMELEN];\r
- pathname_t newname;\r
- int rval;\r
-\r
- rval = rmdir(name->path);\r
- if (rval >= 0 || errno != RED_ENAMETOOLONG)\r
- return rval;\r
- separate_pathname(name, buf, &newname);\r
- if (chdir(buf) == 0) {\r
- rval = rmdir_path(&newname);\r
- chdir("..");\r
- }\r
- free_pathname(&newname);\r
- return rval;\r
-}\r
-\r
-static void separate_pathname(pathname_t *name, char *buf, pathname_t *newname)\r
-{\r
- char *slash;\r
-\r
- init_pathname(newname);\r
- slash = strchr(name->path, '/');\r
- if (slash == NULL) {\r
- buf[0] = '\0';\r
- return;\r
- }\r
- *slash = '\0';\r
- strcpy(buf, name->path);\r
- *slash = '/';\r
- append_pathname(newname, slash + 1);\r
-}\r
-\r
-static int stat64_path(pathname_t *name, REDSTAT *sbuf)\r
-{\r
- char buf[MAXNAMELEN];\r
- pathname_t newname;\r
- int rval;\r
-\r
- rval = stat64(name->path, sbuf);\r
- if (rval >= 0 || errno != RED_ENAMETOOLONG)\r
- return rval;\r
- separate_pathname(name, buf, &newname);\r
- if (chdir(buf) == 0) {\r
- rval = stat64_path(&newname, sbuf);\r
- chdir("..");\r
- }\r
- free_pathname(&newname);\r
- return rval;\r
-}\r
-\r
-static int truncate64_path(pathname_t *name, off64_t length)\r
-{\r
- char buf[MAXNAMELEN];\r
- pathname_t newname;\r
- int rval;\r
-\r
- rval = truncate64(name->path, length);\r
- if (rval >= 0 || errno != RED_ENAMETOOLONG)\r
- return rval;\r
- separate_pathname(name, buf, &newname);\r
- if (chdir(buf) == 0) {\r
- rval = truncate64_path(&newname, length);\r
- chdir("..");\r
- }\r
- free_pathname(&newname);\r
- return rval;\r
-}\r
-\r
-static int unlink_path(pathname_t *name)\r
-{\r
- char buf[MAXNAMELEN];\r
- pathname_t newname;\r
- int rval;\r
-\r
- rval = unlink(name->path);\r
- if (rval >= 0 || errno != RED_ENAMETOOLONG)\r
- return rval;\r
- separate_pathname(name, buf, &newname);\r
- if (chdir(buf) == 0) {\r
- rval = unlink_path(&newname);\r
- chdir("..");\r
- }\r
- free_pathname(&newname);\r
- return rval;\r
-}\r
-\r
-static void usage(const char *progname)\r
-{\r
- RedPrintf("usage: %s VolumeID [Options]\n", progname);\r
- RedPrintf("File system stress test.\n\n");\r
- RedPrintf("Where:\n");\r
- RedPrintf(" VolumeID\n");\r
- RedPrintf(" A volume number (e.g., 2) or a volume path prefix (e.g., VOL1: or /data)\n");\r
- RedPrintf(" of the volume to test.\n");\r
- RedPrintf("And 'Options' are any of the following:\n");\r
- RedPrintf(" --no-cleanup, -c\n");\r
- RedPrintf(" Specifies not to remove files (cleanup) after execution\n");\r
- RedPrintf(" --loops=count, -l count\n");\r
- RedPrintf(" Specifies the number of times the entire test should loop. Use 0 for\n");\r
- RedPrintf(" infinite. Default 1.\n");\r
- RedPrintf(" --nops=count, -n count\n");\r
- RedPrintf(" Specifies the number of operations to run (default 10000).\n");\r
- RedPrintf(" --namepad, -r\n");\r
- RedPrintf(" Specifies to use random name padding (resulting in longer names).\n");\r
- RedPrintf(" --seed=value, -s value\n");\r
- RedPrintf(" Specifies the seed for the random number generator (default timestamp).\n");\r
- RedPrintf(" --verbose, -v\n");\r
- RedPrintf(" Specifies verbose mode (without this, test is very quiet).\n");\r
- RedPrintf(" --dev=devname, -D devname\n");\r
- RedPrintf(" Specifies the device name. This is typically only meaningful when\n");\r
- RedPrintf(" running the test on a host machine. This can be \"ram\" to test on a RAM\n");\r
- RedPrintf(" disk, the path and name of a file disk (e.g., red.bin); or an OS-specific\n");\r
- RedPrintf(" reference to a device (on Windows, a drive letter like G: or a device name\n");\r
- RedPrintf(" like \\\\.\\PhysicalDrive7).\n");\r
- RedPrintf(" --help, -H\n");\r
- RedPrintf(" Prints this usage text and exits.\n\n");\r
- RedPrintf("Warning: This test will format the volume -- destroying all existing data.\n\n");\r
-}\r
-\r
-static void creat_f(int opno, long r)\r
-{\r
- int e;\r
- int e1;\r
- pathname_t f;\r
- int fd;\r
- fent_t *fep;\r
- int id;\r
- int parid;\r
- int type;\r
- int v;\r
- int v1;\r
- int esz = 0;\r
-\r
- if (!get_fname(FT_DIRm, r, NULL, NULL, &fep, &v1))\r
- parid = -1;\r
- else\r
- parid = fep->id;\r
- init_pathname(&f);\r
- type = rtpct ? ((int)(random() % 100) > rtpct ? FT_REG : FT_RTF) : FT_REG;\r
- e = generate_fname(fep, type, &f, &id, &v);\r
- v |= v1;\r
- if (!e) {\r
- if (v) {\r
- fent_to_name(&f, &flist[FT_DIR], fep);\r
- RedPrintf("%d/%d: creat - no filename from %s\n",\r
- procid, opno, f.path);\r
- }\r
- free_pathname(&f);\r
- return;\r
- }\r
- fd = creat_path(&f, 0666);\r
- e = fd < 0 ? errno : 0;\r
- e1 = 0;\r
- check_cwd();\r
- esz = 0;\r
- if (fd >= 0) {\r
- add_to_flist(type, id, parid);\r
- close(fd);\r
- }\r
- if (v)\r
- RedPrintf("%d/%d: creat %s x:%d %d %d\n", procid, opno, f.path,\r
- esz, e, e1);\r
- free_pathname(&f);\r
-}\r
-\r
-static void fdatasync_f(int opno, long r)\r
-{\r
- int e;\r
- pathname_t f;\r
- int fd;\r
- int v;\r
-\r
- init_pathname(&f);\r
- if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {\r
- if (v)\r
- RedPrintf("%d/%d: fdatasync - no filename\n",\r
- procid, opno);\r
- free_pathname(&f);\r
- return;\r
- }\r
- fd = open_path(&f, O_WRONLY);\r
- e = fd < 0 ? errno : 0;\r
- check_cwd();\r
- if (fd < 0) {\r
- if (v)\r
- RedPrintf("%d/%d: fdatasync - open %s failed %d\n",\r
- procid, opno, f.path, e);\r
- free_pathname(&f);\r
- return;\r
- }\r
- e = fdatasync(fd) < 0 ? errno : 0;\r
- if (v)\r
- RedPrintf("%d/%d: fdatasync %s %d\n", procid, opno, f.path, e);\r
- free_pathname(&f);\r
- close(fd);\r
-}\r
-\r
-static void fsync_f(int opno, long r)\r
-{\r
- int e;\r
- pathname_t f;\r
- int fd;\r
- int v;\r
-\r
- init_pathname(&f);\r
- if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {\r
- if (v)\r
- RedPrintf("%d/%d: fsync - no filename\n", procid, opno);\r
- free_pathname(&f);\r
- return;\r
- }\r
- fd = open_path(&f, O_WRONLY);\r
- e = fd < 0 ? errno : 0;\r
- check_cwd();\r
- if (fd < 0) {\r
- if (v)\r
- RedPrintf("%d/%d: fsync - open %s failed %d\n",\r
- procid, opno, f.path, e);\r
- free_pathname(&f);\r
- return;\r
- }\r
- e = fsync(fd) < 0 ? errno : 0;\r
- if (v)\r
- RedPrintf("%d/%d: fsync %s %d\n", procid, opno, f.path, e);\r
- free_pathname(&f);\r
- close(fd);\r
-}\r
-\r
-static void getdents_f(int opno, long r)\r
-{\r
- DIR *dir;\r
- pathname_t f;\r
- int v;\r
-\r
- init_pathname(&f);\r
- if (!get_fname(FT_DIRm, r, &f, NULL, NULL, &v))\r
- append_pathname(&f, ".");\r
- dir = opendir_path(&f);\r
- check_cwd();\r
- if (dir == NULL) {\r
- if (v)\r
- RedPrintf("%d/%d: getdents - can't open %s\n",\r
- procid, opno, f.path);\r
- free_pathname(&f);\r
- return;\r
- }\r
- while (readdir64(dir) != NULL)\r
- continue;\r
- if (v)\r
- RedPrintf("%d/%d: getdents %s 0\n", procid, opno, f.path);\r
- free_pathname(&f);\r
- closedir(dir);\r
-}\r
-\r
-static void link_f(int opno, long r)\r
-{\r
- int e;\r
- pathname_t f;\r
- fent_t *fep;\r
- flist_t *flp;\r
- int id;\r
- pathname_t l;\r
- int parid;\r
- int v;\r
- int v1;\r
-\r
- init_pathname(&f);\r
- if (!get_fname(FT_NOTDIR, r, &f, &flp, NULL, &v1)) {\r
- if (v1)\r
- RedPrintf("%d/%d: link - no file\n", procid, opno);\r
- free_pathname(&f);\r
- return;\r
- }\r
- if (!get_fname(FT_DIRm, random(), NULL, NULL, &fep, &v))\r
- parid = -1;\r
- else\r
- parid = fep->id;\r
- v |= v1;\r
- init_pathname(&l);\r
- e = generate_fname(fep, (int)(flp - flist), &l, &id, &v1);\r
- v |= v1;\r
- if (!e) {\r
- if (v) {\r
- fent_to_name(&l, &flist[FT_DIR], fep);\r
- RedPrintf("%d/%d: link - no filename from %s\n",\r
- procid, opno, l.path);\r
- }\r
- free_pathname(&l);\r
- free_pathname(&f);\r
- return;\r
- }\r
- e = link_path(&f, &l) < 0 ? errno : 0;\r
- check_cwd();\r
- if (e == 0)\r
- add_to_flist((int)(flp - flist), id, parid);\r
- if (v)\r
- RedPrintf("%d/%d: link %s %s %d\n", procid, opno, f.path, l.path,\r
- e);\r
- free_pathname(&l);\r
- free_pathname(&f);\r
-}\r
-\r
-static void mkdir_f(int opno, long r)\r
-{\r
- int e;\r
- pathname_t f;\r
- fent_t *fep;\r
- int id;\r
- int parid;\r
- int v;\r
- int v1;\r
-\r
- if (!get_fname(FT_DIRm, r, NULL, NULL, &fep, &v))\r
- parid = -1;\r
- else\r
- parid = fep->id;\r
- init_pathname(&f);\r
- e = generate_fname(fep, FT_DIR, &f, &id, &v1);\r
- v |= v1;\r
- if (!e) {\r
- if (v) {\r
- fent_to_name(&f, &flist[FT_DIR], fep);\r
- RedPrintf("%d/%d: mkdir - no filename from %s\n",\r
- procid, opno, f.path);\r
- }\r
- free_pathname(&f);\r
- return;\r
- }\r
- e = mkdir_path(&f, 0777) < 0 ? errno : 0;\r
- check_cwd();\r
- if (e == 0)\r
- add_to_flist(FT_DIR, id, parid);\r
- if (v)\r
- RedPrintf("%d/%d: mkdir %s %d\n", procid, opno, f.path, e);\r
- free_pathname(&f);\r
-}\r
-\r
-static void read_f(int opno, long r)\r
-{\r
- char *buf;\r
- int e;\r
- pathname_t f;\r
- int fd;\r
- uint32_t len;\r
- __int64_t lr;\r
- off64_t off;\r
- REDSTAT stb;\r
- int v;\r
-\r
- init_pathname(&f);\r
- if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {\r
- if (v)\r
- RedPrintf("%d/%d: read - no filename\n", procid, opno);\r
- free_pathname(&f);\r
- return;\r
- }\r
- fd = open_path(&f, O_RDONLY);\r
- e = fd < 0 ? errno : 0;\r
- check_cwd();\r
- if (fd < 0) {\r
- if (v)\r
- RedPrintf("%d/%d: read - open %s failed %d\n",\r
- procid, opno, f.path, e);\r
- free_pathname(&f);\r
- return;\r
- }\r
- if (fstat64(fd, &stb) < 0) {\r
- if (v)\r
- RedPrintf("%d/%d: read - fstat64 %s failed %d\n",\r
- procid, opno, f.path, errno);\r
- free_pathname(&f);\r
- close(fd);\r
- return;\r
- }\r
- if (stb.st_size == 0) {\r
- if (v)\r
- RedPrintf("%d/%d: read - %s zero size\n", procid, opno,\r
- f.path);\r
- free_pathname(&f);\r
- close(fd);\r
- return;\r
- }\r
- lr = ((__int64_t) random() << 32) + random();\r
- off = (off64_t) (lr % stb.st_size);\r
- lseek64(fd, off, SEEK_SET);\r
- len = (random() % (getpagesize() * 4)) + 1;\r
- buf = malloc(len);\r
- e = read(fd, buf, len) < 0 ? errno : 0;\r
- free(buf);\r
- if (v)\r
- RedPrintf("%d/%d: read %s [%lld,%ld] %d\n",\r
- procid, opno, f.path, (long long)off, (long int)len, e);\r
- free_pathname(&f);\r
- close(fd);\r
-}\r
-\r
-static void rename_f(int opno, long r)\r
-{\r
- fent_t *dfep;\r
- int e;\r
- pathname_t f;\r
- fent_t *fep;\r
- flist_t *flp;\r
- int id;\r
- pathname_t newf;\r
- int oldid;\r
- int parid;\r
- int v;\r
- int v1;\r
-\r
- init_pathname(&f);\r
- if (!get_fname(FT_ANYm, r, &f, &flp, &fep, &v1)) {\r
- if (v1)\r
- RedPrintf("%d/%d: rename - no filename\n", procid, opno);\r
- free_pathname(&f);\r
- return;\r
- }\r
- if (!get_fname(FT_DIRm, random(), NULL, NULL, &dfep, &v))\r
- parid = -1;\r
- else\r
- parid = dfep->id;\r
- v |= v1;\r
- init_pathname(&newf);\r
- e = generate_fname(dfep, (int)(flp - flist), &newf, &id, &v1);\r
- v |= v1;\r
- if (!e) {\r
- if (v) {\r
- fent_to_name(&f, &flist[FT_DIR], dfep);\r
- RedPrintf("%d/%d: rename - no filename from %s\n",\r
- procid, opno, f.path);\r
- }\r
- free_pathname(&newf);\r
- free_pathname(&f);\r
- return;\r
- }\r
- e = rename_path(&f, &newf) < 0 ? errno : 0;\r
- check_cwd();\r
- if (e == 0) {\r
- if (flp - flist == FT_DIR) {\r
- oldid = fep->id;\r
- fix_parent(oldid, id);\r
- }\r
- del_from_flist((int)(flp - flist), (int)(fep - flp->fents));\r
- add_to_flist((int)(flp - flist), id, parid);\r
- }\r
- if (v)\r
- RedPrintf("%d/%d: rename %s to %s %d\n", procid, opno, f.path,\r
- newf.path, e);\r
- free_pathname(&newf);\r
- free_pathname(&f);\r
-}\r
-\r
-static void rmdir_f(int opno, long r)\r
-{\r
- int e;\r
- pathname_t f;\r
- fent_t *fep;\r
- int v;\r
-\r
- init_pathname(&f);\r
- if (!get_fname(FT_DIRm, r, &f, NULL, &fep, &v)) {\r
- if (v)\r
- RedPrintf("%d/%d: rmdir - no directory\n", procid, opno);\r
- free_pathname(&f);\r
- return;\r
- }\r
- e = rmdir_path(&f) < 0 ? errno : 0;\r
- check_cwd();\r
- if (e == 0)\r
- del_from_flist(FT_DIR, (int)(fep - flist[FT_DIR].fents));\r
- if (v)\r
- RedPrintf("%d/%d: rmdir %s %d\n", procid, opno, f.path, e);\r
- free_pathname(&f);\r
-}\r
-\r
-static void stat_f(int opno, long r)\r
-{\r
- int e;\r
- pathname_t f;\r
- REDSTAT stb;\r
- int v;\r
-\r
- init_pathname(&f);\r
- if (!get_fname(FT_ANYm, r, &f, NULL, NULL, &v)) {\r
- if (v)\r
- RedPrintf("%d/%d: stat - no entries\n", procid, opno);\r
- free_pathname(&f);\r
- return;\r
- }\r
- e = lstat64_path(&f, &stb) < 0 ? errno : 0;\r
- check_cwd();\r
- if (v)\r
- RedPrintf("%d/%d: stat %s %d\n", procid, opno, f.path, e);\r
- free_pathname(&f);\r
-}\r
-\r
-static void truncate_f(int opno, long r)\r
-{\r
- int e;\r
- pathname_t f;\r
- __int64_t lr;\r
- off64_t off;\r
- REDSTAT stb;\r
- int v;\r
-\r
- init_pathname(&f);\r
- if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {\r
- if (v)\r
- RedPrintf("%d/%d: truncate - no filename\n", procid, opno);\r
- free_pathname(&f);\r
- return;\r
- }\r
- e = stat64_path(&f, &stb) < 0 ? errno : 0;\r
- check_cwd();\r
- if (e > 0) {\r
- if (v)\r
- RedPrintf("%d/%d: truncate - stat64 %s failed %d\n",\r
- procid, opno, f.path, e);\r
- free_pathname(&f);\r
- return;\r
- }\r
- lr = ((__int64_t) random() << 32) + random();\r
- off = lr % MIN(stb.st_size + (1024 * 1024), MAXFSIZE);\r
- off %= maxfsize;\r
- e = truncate64_path(&f, off) < 0 ? errno : 0;\r
- check_cwd();\r
- if (v)\r
- RedPrintf("%d/%d: truncate %s %lld %d\n", procid, opno, f.path,\r
- (long long)off, e);\r
- free_pathname(&f);\r
-}\r
-\r
-static void unlink_f(int opno, long r)\r
-{\r
- int e;\r
- pathname_t f;\r
- fent_t *fep;\r
- flist_t *flp;\r
- int v;\r
-\r
- init_pathname(&f);\r
- if (!get_fname(FT_NOTDIR, r, &f, &flp, &fep, &v)) {\r
- if (v)\r
- RedPrintf("%d/%d: unlink - no file\n", procid, opno);\r
- free_pathname(&f);\r
- return;\r
- }\r
- e = unlink_path(&f) < 0 ? errno : 0;\r
- check_cwd();\r
- if (e == 0)\r
- del_from_flist((int)(flp - flist), (int)(fep - flp->fents));\r
- if (v)\r
- RedPrintf("%d/%d: unlink %s %d\n", procid, opno, f.path, e);\r
- free_pathname(&f);\r
-}\r
-\r
-static void write_f(int opno, long r)\r
-{\r
- char *buf;\r
- int e;\r
- pathname_t f;\r
- int fd;\r
- uint32_t len;\r
- __int64_t lr;\r
- off64_t off;\r
- REDSTAT stb;\r
- int v;\r
-\r
- init_pathname(&f);\r
- if (!get_fname(FT_REGm, r, &f, NULL, NULL, &v)) {\r
- if (v)\r
- RedPrintf("%d/%d: write - no filename\n", procid, opno);\r
- free_pathname(&f);\r
- return;\r
- }\r
- fd = open_path(&f, O_WRONLY);\r
- e = fd < 0 ? errno : 0;\r
- check_cwd();\r
- if (fd < 0) {\r
- if (v)\r
- RedPrintf("%d/%d: write - open %s failed %d\n",\r
- procid, opno, f.path, e);\r
- free_pathname(&f);\r
- return;\r
- }\r
- if (fstat64(fd, &stb) < 0) {\r
- if (v)\r
- RedPrintf("%d/%d: write - fstat64 %s failed %d\n",\r
- procid, opno, f.path, errno);\r
- free_pathname(&f);\r
- close(fd);\r
- return;\r
- }\r
- lr = ((__int64_t) random() << 32) + random();\r
- off = (off64_t) (lr % MIN(stb.st_size + (1024 * 1024), MAXFSIZE));\r
- off %= maxfsize;\r
- lseek64(fd, off, SEEK_SET);\r
- len = (random() % (getpagesize() * 4)) + 1;\r
- buf = malloc(len);\r
- memset(buf, nameseq & 0xff, len);\r
- e = write(fd, buf, len) < 0 ? errno : 0;\r
- free(buf);\r
- if (v)\r
- RedPrintf("%d/%d: write %s [%lld,%ld] %d\n",\r
- procid, opno, f.path, (long long)off, (long int)len, e);\r
- free_pathname(&f);\r
- close(fd);\r
-}\r
-\r
-\r
-#if REDCONF_CHECKER == 1\r
-static void check_f(int opno, long r)\r
-{\r
- int32_t ret;\r
- const char *pszVolume = gpRedVolConf->pszPathPrefix;\r
-\r
- (void)r;\r
-\r
- errno = 0;\r
-\r
- ret = red_transact(pszVolume);\r
-\r
- if(ret == 0)\r
- {\r
- ret = red_umount(pszVolume);\r
-\r
- if(ret == 0)\r
- {\r
- int32_t ret2;\r
-\r
- errno = -RedCoreVolCheck();\r
- if(errno != 0)\r
- {\r
- ret = -1;\r
- }\r
-\r
- ret2 = red_mount(pszVolume);\r
-\r
- if(ret == 0)\r
- {\r
- ret = ret2;\r
- }\r
-\r
- if(ret2 != 0)\r
- {\r
- exit(1);\r
- }\r
- }\r
- }\r
-\r
- if (verbose)\r
- {\r
- RedPrintf("%d/%d: check %s %d\n", procid, opno, pszVolume, errno);\r
- }\r
-}\r
-#endif\r
-\r
-\r
-#endif /* FSSTRESS_SUPPORTED */\r
-\r
+/*
+ * Copyright (c) 2000 Silicon Graphics, 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 would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like. Any license provided herein, whether implied or
+ * otherwise, applies only to this software file. Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA 94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+/** @file
+ @brief File system stress test.
+
+ This version of SGI fsstress has been modified to be single-threaded and to
+ work with the Reliance Edge POSIX-like API.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include <stdarg.h>
+#include <time.h>
+
+#include <redposix.h>
+#include <redtests.h>
+
+#if FSSTRESS_SUPPORTED
+
+#include "redposixcompat.h"
+
+#include <redosserv.h>
+#include <redutils.h>
+#include <redmacs.h>
+#include <redvolume.h>
+#include <redgetopt.h>
+#include <redtoolcmn.h>
+
+#if REDCONF_CHECKER == 1
+#include <redcoreapi.h>
+#endif
+
+
+/* Create POSIX types. Use #define to avoid name conflicts in those
+ environments where the type names already exist.
+*/
+#define off_t int64_t
+#define off64_t off_t
+#define ino_t uint32_t
+#define mode_t uint16_t
+#define __int64_t int64_t
+
+
+/** @brief Generate a random number.
+
+ @return A nonnegative random number.
+*/
+#define random() ((int)(RedRand32(NULL) & 0x7FFFFFFF))
+
+
+/** @brief Seed the random number generator.
+*/
+#define srandom(seed) RedRandSeed(seed)
+
+
+#define _exit(status) exit(status)
+#define getpagesize() 4096U
+#define getpid() 1
+
+
+/** @brief Determine the maximum file size.
+
+ This is used for the MAXFSSIZE macro.
+*/
+static uint64_t MaxFileSize(void)
+{
+ REDSTATFS info;
+ int32_t iStatus;
+ REDSTATUS errnoSave = errno;
+ uint64_t ullMaxFileSize;
+
+ iStatus = red_statvfs("", &info);
+ if(iStatus == 0)
+ {
+ ullMaxFileSize = info.f_maxfsize;
+ }
+ else
+ {
+ /* This function does not change errno.
+ */
+ errno = errnoSave;
+
+ ullMaxFileSize = 0x7FFFFFFFU;
+ }
+
+ return ullMaxFileSize;
+}
+
+
+/*-------------------------------------------------------------------
+ Simulated current working directory support
+-------------------------------------------------------------------*/
+
+
+/* Forward declaration for red_chdir().
+*/
+static int red_stat(const char *pszPath, REDSTAT *pStat);
+
+/* The simulated CWD functions.
+*/
+#undef chdir
+#undef getcwd
+#define chdir(path) red_chdir(path)
+#define getcwd(buf, size) red_getcwd(buf, size)
+
+
+/* Redefine the path-based APIs to call MakeFullPath() on their arguments
+ since there is no CWD support in the red_*() APIs.
+*/
+#undef open
+#undef unlink
+#undef mkdir
+#undef rmdir
+#undef rename
+#undef link
+#undef opendir
+#define open(path, oflag) red_open(MakeFullPath(path), oflag)
+#define unlink(path) red_unlink(MakeFullPath(path))
+#define mkdir(path) red_mkdir(MakeFullPath(path))
+#define rmdir(path) red_rmdir(MakeFullPath(path))
+#define rename(old, new) red_rename(MakeFullPath(old), MakeFullPath(new))
+#define link(path, hardlink) red_link(MakeFullPath(path), MakeFullPath(hardlink))
+#define opendir(path) red_opendir(MakeFullPath(path))
+
+#define FSSTRESS_BUF_SIZE 1024U
+
+/* Stores the simulated current working directory.
+*/
+static char szLocalCwd[FSSTRESS_BUF_SIZE] = "/";
+
+
+/** @brief Change the current working directory.
+
+ This function only supports a subset of what is possible with POSIX chdir().
+
+ @param pszPath The new current working directory.
+
+ @return Upon successful completion, 0 shall be returned. Otherwise, -1
+ shall be returned, and errno shall be set to indicate the error.
+*/
+static int red_chdir(
+ const char *pszPath)
+{
+ uint32_t ulIdx;
+ int iErrno = 0;
+
+ if(strcmp(pszPath, "..") == 0)
+ {
+ uint32_t ulLastSlashIdx = 0U;
+
+ /* Chop off the last path separator and everything after it, so that
+ "/foo/bar/baz" becomes "/foo/bar", moving the CWD up one directory.
+ */
+ for(ulIdx = 0U; szLocalCwd[ulIdx] != '\0'; ulIdx++)
+ {
+ if(szLocalCwd[ulIdx] == '/')
+ {
+ ulLastSlashIdx = ulIdx;
+ }
+ }
+
+ if(ulLastSlashIdx != 0U)
+ {
+ szLocalCwd[ulLastSlashIdx] = '\0';
+ }
+ }
+ else
+ {
+ char szOldCwd[FSSTRESS_BUF_SIZE];
+
+ /* chdir() must have no effect on the CWD if it fails, so save the CWD
+ so we can revert it if necessary.
+ */
+ strcpy(szOldCwd, szLocalCwd);
+
+ if(pszPath[0U] == '/')
+ {
+ if(strlen(pszPath) >= sizeof(szLocalCwd))
+ {
+ iErrno = RED_ENAMETOOLONG;
+ }
+ else
+ {
+ strcpy(szLocalCwd, pszPath);
+ }
+ }
+ else
+ {
+ ulIdx = strlen(szLocalCwd);
+
+ if((ulIdx + 1U + strlen(pszPath)) >= sizeof(szLocalCwd))
+ {
+ iErrno = RED_ENAMETOOLONG;
+ }
+ else
+ {
+ if(szLocalCwd[1U] != '\0')
+ {
+ szLocalCwd[ulIdx] = '/';
+ ulIdx++;
+ }
+
+ strcpy(&szLocalCwd[ulIdx], pszPath);
+ }
+ }
+
+ if(iErrno == 0)
+ {
+ REDSTAT s;
+ int iStatus;
+
+ iStatus = red_stat(szLocalCwd, &s);
+ if(iStatus != 0)
+ {
+ iErrno = errno;
+ }
+ else if(!S_ISDIR(s.st_mode))
+ {
+ iErrno = RED_ENOTDIR;
+ }
+ else
+ {
+ /* No error, new CWD checks out.
+ */
+ }
+ }
+
+ if(iErrno != 0)
+ {
+ strcpy(szLocalCwd, szOldCwd);
+ }
+ }
+
+ if(iErrno != 0)
+ {
+ errno = iErrno;
+ }
+
+ return iErrno == 0 ? 0 : -1;
+}
+
+
+/** @brief Retrieve the current working directory.
+
+ @param pszBuf On successful return, populated with the current working
+ directory. If NULL, memory will be allocated for the CWD
+ and returned by this function.
+ @param nSize The size of @p pszBuf.
+
+ @return On success, if @p pszBuf was non-NULL, returns @p pszBuf; if
+ @p pszBuf was NULL, returns an allocated buffer populated with the
+ CWD which must be freed by the caller. On failure, returns NULL
+ and errno will be set.
+*/
+static char *red_getcwd(
+ char *pszBuf,
+ size_t nSize)
+{
+ char *pszRet;
+
+ if(pszBuf == NULL)
+ {
+ pszRet = malloc(strlen(szLocalCwd) + 1U);
+ if(pszRet == NULL)
+ {
+ errno = RED_ENOMEM;
+ }
+ else
+ {
+ strcpy(pszRet, szLocalCwd);
+ }
+ }
+ else if(nSize < strlen(szLocalCwd) + 1U)
+ {
+ errno = RED_ERANGE;
+ pszRet = NULL;
+ }
+ else
+ {
+ strcpy(pszBuf, szLocalCwd);
+ pszRet = pszBuf;
+ }
+
+ return pszRet;
+}
+
+
+/** @brief Make a relative path into a fully qualified path.
+
+ @param pszName The relative path.
+
+ @return On success, a pointer to a fully qualified path. On error, NULL.
+*/
+static const char *MakeFullPath(
+ const char *pszName)
+{
+ #define MAXVOLNAME 64U /* Enough for most configs. */
+ static char aszFullPath[2U][MAXVOLNAME + 1U + FSSTRESS_BUF_SIZE];
+ static uint32_t ulWhich = 0U;
+
+ char *pszFullPath = aszFullPath[ulWhich];
+ const char *pszVolume = gpRedVolConf->pszPathPrefix;
+ int32_t iLen;
+
+ if(pszName[0U] == '/')
+ {
+ iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s%s", pszVolume, pszName);
+ }
+ else if(strcmp(pszName, ".") == 0U)
+ {
+ iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s%s", pszVolume, szLocalCwd);
+ }
+ else if((szLocalCwd[0U] == '/') && (szLocalCwd[1U] == '\0'))
+ {
+ iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s/%s", pszVolume, pszName);
+ }
+ else
+ {
+ iLen = RedSNPrintf(pszFullPath, sizeof(aszFullPath[0U]), "%s%s/%s", pszVolume, szLocalCwd, pszName);
+ }
+
+ if(iLen == -1)
+ {
+ /* Insufficient path buffer space.
+ */
+ pszFullPath = NULL;
+ }
+ else
+ {
+ /* Toggle between two full path arrays; a kluge to make rename() and
+ link() work correctly.
+ */
+ ulWhich ^= 1U;
+ }
+
+ return pszFullPath;
+}
+
+
+/*-------------------------------------------------------------------
+ POSIX functions not implemented by the RED POSIX-like API
+-------------------------------------------------------------------*/
+
+#define stat(p, s) red_stat(p, s)
+#define stat64(p, s) stat(p, s)
+#define lstat(p, s) stat(p, s)
+#define lstat64(p, s) stat(p, s)
+#define truncate(p, s) red_truncate(p, s)
+#define truncate64(p, s) truncate(p, s)
+
+
+/** @brief Get the status of a file or directory.
+*/
+static int red_stat(
+ const char *pszPath,
+ REDSTAT *pStat)
+{
+ int iFd;
+ int iRet;
+
+ iFd = open(pszPath, O_RDONLY);
+ iRet = iFd;
+ if(iFd != -1)
+ {
+ iRet = fstat(iFd, pStat);
+
+ (void)close(iFd);
+ }
+
+ return iRet;
+}
+
+
+/** @brief Truncate a file to a specified length.
+*/
+static int red_truncate(
+ const char *pszPath,
+ off_t llSize)
+{
+ int iFd;
+ int iRet;
+
+ iFd = open(pszPath, O_WRONLY);
+ iRet = iFd;
+ if(iFd != -1)
+ {
+ iRet = ftruncate(iFd, llSize);
+
+ (void)close(iFd);
+ }
+
+ return iRet;
+}
+
+
+/*-------------------------------------------------------------------
+ Begin ported fsstress code
+-------------------------------------------------------------------*/
+
+/* Stuff from xfscompat.h */
+
+#define MAXNAMELEN (REDCONF_NAME_MAX+1U) /* Assumed to include NUL */
+
+struct dioattr {
+ int d_miniosz, d_maxiosz, d_mem;
+};
+
+#define MIN(a,b) ((a)<(b) ? (a):(b))
+#define MAX(a,b) ((a)>(b) ? (a):(b))
+
+/* End xfscompat.h */
+
+
+typedef enum {
+ OP_CREAT,
+ OP_FDATASYNC,
+ OP_FSYNC,
+ OP_GETDENTS,
+ OP_LINK,
+ OP_MKDIR,
+ OP_READ,
+ OP_RENAME,
+ OP_RMDIR,
+ OP_STAT,
+ OP_TRUNCATE,
+ OP_UNLINK,
+ OP_WRITE,
+ #if REDCONF_CHECKER == 1
+ OP_CHECK,
+ #endif
+ OP_LAST
+} opty_t;
+
+typedef void (*opfnc_t) (int, long);
+
+typedef struct opdesc {
+ opty_t op;
+ const char *name;
+ opfnc_t func;
+ int freq;
+ int iswrite;
+} opdesc_t;
+
+typedef struct fent {
+ int id;
+ int parent;
+} fent_t;
+
+typedef struct flist {
+ int nfiles;
+ int nslots;
+ int tag;
+ fent_t *fents;
+} flist_t;
+
+typedef struct pathname {
+ int len;
+ char *path;
+} pathname_t;
+
+#define FT_DIR 0
+#define FT_DIRm (1 << FT_DIR)
+#define FT_REG 1
+#define FT_REGm (1 << FT_REG)
+#define FT_SYM 2
+#define FT_SYMm (1 << FT_SYM)
+#define FT_DEV 3
+#define FT_DEVm (1 << FT_DEV)
+#define FT_RTF 4
+#define FT_RTFm (1 << FT_RTF)
+#define FT_nft 5
+#define FT_ANYm ((1 << FT_nft) - 1)
+#define FT_REGFILE (FT_REGm | FT_RTFm)
+#define FT_NOTDIR (FT_ANYm & ~FT_DIRm)
+
+#define FLIST_SLOT_INCR 16
+#define NDCACHE 64
+
+#define MAXFSIZE MaxFileSize()
+
+static void creat_f(int opno, long r);
+static void fdatasync_f(int opno, long r);
+static void fsync_f(int opno, long r);
+static void getdents_f(int opno, long r);
+static void link_f(int opno, long r);
+static void mkdir_f(int opno, long r);
+static void read_f(int opno, long r);
+static void rename_f(int opno, long r);
+static void rmdir_f(int opno, long r);
+static void stat_f(int opno, long r);
+static void truncate_f(int opno, long r);
+static void unlink_f(int opno, long r);
+static void write_f(int opno, long r);
+#if REDCONF_CHECKER == 1
+static void check_f(int opno, long r);
+#endif
+
+static opdesc_t ops[] = {
+ {OP_CREAT, "creat", creat_f, 4, 1},
+ {OP_FDATASYNC, "fdatasync", fdatasync_f, 1, 1},
+ {OP_FSYNC, "fsync", fsync_f, 1, 1},
+ {OP_GETDENTS, "getdents", getdents_f, 1, 0},
+ {OP_LINK, "link", link_f, 1, 1},
+ {OP_MKDIR, "mkdir", mkdir_f, 2, 1},
+ {OP_READ, "read", read_f, 1, 0},
+ {OP_RENAME, "rename", rename_f, 2, 1},
+ {OP_RMDIR, "rmdir", rmdir_f, 1, 1},
+ {OP_STAT, "stat", stat_f, 1, 0},
+ {OP_TRUNCATE, "truncate", truncate_f, 2, 1},
+ {OP_UNLINK, "unlink", unlink_f, 1, 1},
+ {OP_WRITE, "write", write_f, 4, 1},
+ #if REDCONF_CHECKER == 1
+ {OP_CHECK, "check", check_f, 1, 1},
+ #endif
+}, *ops_end;
+
+static flist_t flist[FT_nft] = {
+ {0, 0, 'd', NULL},
+ {0, 0, 'f', NULL},
+ {0, 0, 'l', NULL},
+ {0, 0, 'c', NULL},
+ {0, 0, 'r', NULL},
+};
+
+static int dcache[NDCACHE];
+static opty_t *freq_table;
+static int freq_table_size;
+static char *homedir;
+static int *ilist;
+static int ilistlen;
+static off64_t maxfsize;
+static int namerand;
+static int nameseq;
+static int nops;
+static int operations = 1;
+static int procid;
+static int rtpct;
+static unsigned long seed = 0;
+static ino_t top_ino;
+static int verbose = 0;
+
+static int delete_tree(const char *path);
+static void add_to_flist(int fd, int it, int parent);
+static void append_pathname(pathname_t *name, const char *str);
+static void check_cwd(void);
+static int creat_path(pathname_t *name, mode_t mode);
+static void dcache_enter(int dirid, int slot);
+static void dcache_init(void);
+static fent_t *dcache_lookup(int dirid);
+static void dcache_purge(int dirid);
+static void del_from_flist(int ft, int slot);
+static void doproc(void);
+static void fent_to_name(pathname_t *name, flist_t *flp, fent_t *fep);
+static void fix_parent(int oldid, int newid);
+static void free_pathname(pathname_t *name);
+static int generate_fname(fent_t *fep, int ft, pathname_t *name, int *idp, int *v);
+static int get_fname(int which, long r, pathname_t *name, flist_t **flpp, fent_t **fepp, int *v);
+static void init_pathname(pathname_t *name);
+static int link_path(pathname_t *name1, pathname_t *name2);
+static int lstat64_path(pathname_t *name, REDSTAT *sbuf);
+static void make_freq_table(void);
+static int mkdir_path(pathname_t *name, mode_t mode);
+static void namerandpad(int id, char *buf, int len);
+static int open_path(pathname_t *name, int oflag);
+static DIR *opendir_path(pathname_t *name);
+static int rename_path(pathname_t *name1, pathname_t *name2);
+static int rmdir_path(pathname_t *name);
+static void separate_pathname(pathname_t *name, char *buf, pathname_t *newname);
+static int stat64_path(pathname_t *name, REDSTAT *sbuf);
+static int truncate64_path(pathname_t *name, off64_t length);
+static int unlink_path(pathname_t *name);
+static void usage(const char *progname);
+
+
+/** @brief Parse parameters for fsstress.
+
+ @param argc The number of arguments from main().
+ @param argv The vector of arguments from main().
+ @param pParam Populated with the fsstress parameters.
+ @param pbVolNum If non-NULL, populated with the volume number.
+ @param ppszDevice If non-NULL, populated with the device name argument or
+ NULL if no device argument is provided.
+
+ @return The result of parsing the parameters.
+*/
+PARAMSTATUS FsstressParseParams(
+ int argc,
+ char *argv[],
+ FSSTRESSPARAM *pParam,
+ uint8_t *pbVolNum,
+ const char **ppszDevice)
+{
+ int c;
+ uint8_t bVolNum;
+ const REDOPTION aLongopts[] =
+ {
+ { "no-cleanup", red_no_argument, NULL, 'c' },
+ { "loops", red_required_argument, NULL, 'l' },
+ { "nops", red_required_argument, NULL, 'n' },
+ { "namepad", red_no_argument, NULL, 'r' },
+ { "seed", red_required_argument, NULL, 's' },
+ { "verbose", red_no_argument, NULL, 'v' },
+ { "dev", red_required_argument, NULL, 'D' },
+ { "help", red_no_argument, NULL, 'H' },
+ { NULL }
+ };
+
+ /* If run without parameters, treat as a help request.
+ */
+ if(argc <= 1)
+ {
+ goto Help;
+ }
+
+ /* Assume no device argument to start with.
+ */
+ if(ppszDevice != NULL)
+ {
+ *ppszDevice = NULL;
+ }
+
+ /* Set default parameters.
+ */
+ FsstressDefaultParams(pParam);
+
+ while((c = RedGetoptLong(argc, argv, "cl:n:rs:vD:H", aLongopts, NULL)) != -1)
+ {
+ switch(c)
+ {
+ case 'c': /* --no-cleanup */
+ pParam->fNoCleanup = true;
+ break;
+ case 'l': /* --loops */
+ pParam->ulLoops = RedAtoI(red_optarg);
+ break;
+ case 'n': /* --nops */
+ pParam->ulNops = RedAtoI(red_optarg);
+ break;
+ case 'r': /* --namepad */
+ pParam->fNamePad = true;
+ break;
+ case 's': /* --seed */
+ pParam->ulSeed = RedAtoI(red_optarg);
+ break;
+ case 'v': /* --verbose */
+ pParam->fVerbose = true;
+ break;
+ case 'D': /* --dev */
+ if(ppszDevice != NULL)
+ {
+ *ppszDevice = red_optarg;
+ }
+ break;
+ case 'H': /* --help */
+ goto Help;
+ case '?': /* Unknown or ambiguous option */
+ case ':': /* Option missing required argument */
+ default:
+ goto BadOpt;
+ }
+ }
+
+ /* RedGetoptLong() has permuted argv to move all non-option arguments to
+ the end. We expect to find a volume identifier.
+ */
+ if(red_optind >= argc)
+ {
+ RedPrintf("Missing volume argument\n");
+ goto BadOpt;
+ }
+
+ bVolNum = RedFindVolumeNumber(argv[red_optind]);
+ if(bVolNum == REDCONF_VOLUME_COUNT)
+ {
+ RedPrintf("Error: \"%s\" is not a valid volume identifier.\n", argv[red_optind]);
+ goto BadOpt;
+ }
+
+ if(pbVolNum != NULL)
+ {
+ *pbVolNum = bVolNum;
+ }
+
+ red_optind++; /* Move past volume parameter. */
+ if(red_optind < argc)
+ {
+ int32_t ii;
+
+ for(ii = red_optind; ii < argc; ii++)
+ {
+ RedPrintf("Error: Unexpected command-line argument \"%s\".\n", argv[ii]);
+ }
+
+ goto BadOpt;
+ }
+
+ return PARAMSTATUS_OK;
+
+ BadOpt:
+
+ RedPrintf("%s - invalid parameters\n", argv[0U]);
+ usage(argv[0U]);
+ return PARAMSTATUS_BAD;
+
+ Help:
+
+ usage(argv[0U]);
+ return PARAMSTATUS_HELP;
+}
+
+
+/** @brief Set default fsstress parameters.
+
+ @param pParam Populated with the default fsstress parameters.
+*/
+void FsstressDefaultParams(
+ FSSTRESSPARAM *pParam)
+{
+ RedMemSet(pParam, 0U, sizeof(*pParam));
+ pParam->ulLoops = 1U;
+ pParam->ulNops = 10000U;
+}
+
+
+/** @brief Start fsstress.
+
+ @param pParam fsstress parameters, either from FsstressParseParams() or
+ constructed programatically.
+
+ @return Zero on success, otherwise nonzero.
+*/
+int FsstressStart(
+ const FSSTRESSPARAM *pParam)
+{
+ char buf[10];
+ int fd;
+ int i;
+ int cleanup;
+ int loops;
+ int loopcntr = 1;
+
+ nops = sizeof(ops) / sizeof(ops[0]);
+ ops_end = &ops[nops];
+
+ /* Copy the already-parsed parameters into the traditional variables.
+ */
+ cleanup = pParam->fNoCleanup ? 1 : 0;
+ loops = pParam->ulLoops;
+ operations = pParam->ulNops;
+ namerand = pParam->fNamePad ? 1 : 0;
+ seed = pParam->ulSeed;
+ verbose = pParam->fVerbose ? 1 : 0;
+
+ make_freq_table();
+
+ while ((loopcntr <= loops) || (loops == 0)) {
+ RedSNPrintf(buf, sizeof(buf), "fss%x", getpid());
+ fd = creat(buf, 0666);
+ maxfsize = (off64_t) MAXFSIZE;
+ dcache_init();
+ if (!seed) {
+ seed = (unsigned long)RedOsClockGetTime();
+ RedPrintf("seed = %ld\n", seed);
+ }
+ close(fd);
+ unlink(buf);
+ procid = 0;
+ doproc();
+ if (cleanup == 0) {
+ delete_tree("/");
+ for (i = 0; i < FT_nft; i++) {
+ flist[i].nslots = 0;
+ flist[i].nfiles = 0;
+ free(flist[i].fents);
+ flist[i].fents = NULL;
+ }
+ }
+ loopcntr++;
+ }
+ return 0;
+}
+
+static int delete_tree(const char *path)
+{
+ REDSTAT sb;
+ DIR *dp;
+ REDDIRENT *dep;
+ char *childpath;
+ size_t len;
+ int e;
+
+ e = stat(path, &sb);
+ if (e)
+ return errno;
+
+ if (!S_ISDIR(sb.st_mode))
+ return unlink(path) ? errno : 0;
+
+ dp = opendir(path);
+ if (dp == NULL)
+ return errno;
+
+ while((dep = readdir(dp)) != NULL) {
+ len = strlen(path) + 1 + strlen(dep->d_name) + 1;
+ childpath = malloc(len);
+
+ strcpy(childpath, path);
+ if (childpath[strlen(childpath) - 1] != '/')
+ strcat(childpath, "/");
+ strcat(childpath, dep->d_name);
+
+ e = delete_tree(childpath);
+
+ free(childpath);
+
+ if (e)
+ break;
+ }
+
+ if (e == 0 && strcmp(path, "/") != 0) {
+ e = rmdir(path) ? errno : 0;
+ }
+ closedir(dp);
+ return e;
+}
+
+static void add_to_flist(int ft, int id, int parent)
+{
+ fent_t *fep;
+ flist_t *ftp;
+
+ ftp = &flist[ft];
+ if (ftp->nfiles == ftp->nslots) {
+ ftp->nslots += FLIST_SLOT_INCR;
+ ftp->fents = realloc(ftp->fents, ftp->nslots * sizeof(fent_t));
+ }
+ fep = &ftp->fents[ftp->nfiles++];
+ fep->id = id;
+ fep->parent = parent;
+}
+
+static void append_pathname(pathname_t *name, const char *str)
+{
+ int len;
+
+ len = strlen(str);
+#ifdef DEBUG
+ if (len && *str == '/' && name->len == 0) {
+ RedPrintf("fsstress: append_pathname failure\n");
+ chdir(homedir);
+ abort();
+
+ }
+#endif
+ name->path = realloc(name->path, name->len + 1 + len);
+ strcpy(&name->path[name->len], str);
+ name->len += len;
+}
+
+static void check_cwd(void)
+{
+#ifdef DEBUG
+ REDSTAT statbuf;
+
+ if (stat64(".", &statbuf) == 0 && statbuf.st_ino == top_ino)
+ return;
+ chdir(homedir);
+ RedPrintf("fsstress: check_cwd failure\n");
+ abort();
+
+#endif
+}
+
+static int creat_path(pathname_t *name, mode_t mode)
+{
+ char buf[MAXNAMELEN];
+ pathname_t newname;
+ int rval;
+
+ rval = creat(name->path, mode);
+ if (rval >= 0 || errno != RED_ENAMETOOLONG)
+ return rval;
+ separate_pathname(name, buf, &newname);
+ if (chdir(buf) == 0) {
+ rval = creat_path(&newname, mode);
+ chdir("..");
+ }
+ free_pathname(&newname);
+ return rval;
+}
+
+static void dcache_enter(int dirid, int slot)
+{
+ dcache[dirid % NDCACHE] = slot;
+}
+
+static void dcache_init(void)
+{
+ int i;
+
+ for (i = 0; i < NDCACHE; i++)
+ dcache[i] = -1;
+}
+
+static fent_t *dcache_lookup(int dirid)
+{
+ fent_t *fep;
+ int i;
+
+ i = dcache[dirid % NDCACHE];
+ if (i >= 0 && (fep = &flist[FT_DIR].fents[i])->id == dirid)
+ return fep;
+ return NULL;
+}
+
+static void dcache_purge(int dirid)
+{
+ int *dcp;
+
+ dcp = &dcache[dirid % NDCACHE];
+ if (*dcp >= 0 && flist[FT_DIR].fents[*dcp].id == dirid)
+ *dcp = -1;
+}
+
+static void del_from_flist(int ft, int slot)
+{
+ flist_t *ftp;
+
+ ftp = &flist[ft];
+ if (ft == FT_DIR)
+ dcache_purge(ftp->fents[slot].id);
+ if (slot != ftp->nfiles - 1) {
+ if (ft == FT_DIR)
+ dcache_purge(ftp->fents[ftp->nfiles - 1].id);
+ ftp->fents[slot] = ftp->fents[--ftp->nfiles];
+ } else
+ ftp->nfiles--;
+}
+
+static fent_t *dirid_to_fent(int dirid)
+{
+ fent_t *efep;
+ fent_t *fep;
+ flist_t *flp;
+
+ if ((fep = dcache_lookup(dirid)))
+ return fep;
+ flp = &flist[FT_DIR];
+ for (fep = flp->fents, efep = &fep[flp->nfiles]; fep < efep; fep++) {
+ if (fep->id == dirid) {
+ dcache_enter(dirid, (int)(fep - flp->fents));
+ return fep;
+ }
+ }
+ return NULL;
+}
+
+static void doproc(void)
+{
+ REDSTAT statbuf;
+ char buf[10];
+ int opno;
+ opdesc_t *p;
+
+ RedSNPrintf(buf, sizeof(buf), "p%x", procid);
+ (void)mkdir(buf);
+ if (chdir(buf) < 0 || stat64(".", &statbuf) < 0) {
+ perror(buf);
+ _exit(1);
+ }
+ top_ino = statbuf.st_ino;
+ homedir = getcwd(NULL, 0);
+ seed += procid;
+ srandom(seed);
+ if (namerand)
+ namerand = random();
+ for (opno = 0; opno < operations; opno++) {
+ p = &ops[freq_table[random() % freq_table_size]];
+ if ((unsigned long)p->func < 4096)
+ abort();
+
+ p->func(opno, random());
+ }
+ free(homedir);
+}
+
+static void fent_to_name(pathname_t *name, flist_t *flp, fent_t *fep)
+{
+ char buf[MAXNAMELEN];
+ int i;
+ fent_t *pfep;
+
+ if (fep == NULL)
+ return;
+ if (fep->parent != -1) {
+ pfep = dirid_to_fent(fep->parent);
+ fent_to_name(name, &flist[FT_DIR], pfep);
+ append_pathname(name, "/");
+ }
+ i = RedSNPrintf(buf, sizeof(buf), "%c%x", flp->tag, fep->id);
+ namerandpad(fep->id, buf, i);
+ append_pathname(name, buf);
+}
+
+static void fix_parent(int oldid, int newid)
+{
+ fent_t *fep;
+ flist_t *flp;
+ int i;
+ int j;
+
+ for (i = 0, flp = flist; i < FT_nft; i++, flp++) {
+ for (j = 0, fep = flp->fents; j < flp->nfiles; j++, fep++) {
+ if (fep->parent == oldid)
+ fep->parent = newid;
+ }
+ }
+}
+
+static void free_pathname(pathname_t *name)
+{
+ if (name->path) {
+ free(name->path);
+ name->path = NULL;
+ name->len = 0;
+ }
+}
+
+static int generate_fname(fent_t *fep, int ft, pathname_t *name, int *idp, int *v)
+{
+ char buf[MAXNAMELEN];
+ flist_t *flp;
+ int id;
+ int j;
+ int len;
+
+ flp = &flist[ft];
+ len = RedSNPrintf(buf, sizeof(buf), "%c%x", flp->tag, id = nameseq++);
+ namerandpad(id, buf, len);
+ if (fep) {
+ fent_to_name(name, &flist[FT_DIR], fep);
+ append_pathname(name, "/");
+ }
+ append_pathname(name, buf);
+ *idp = id;
+ *v = verbose;
+ for (j = 0; !*v && j < ilistlen; j++) {
+ if (ilist[j] == id) {
+ *v = 1;
+ break;
+ }
+ }
+ return 1;
+}
+
+static int
+get_fname(int which, long r, pathname_t *name, flist_t **flpp, fent_t **fepp, int *v)
+{
+ int c;
+ fent_t *fep;
+ flist_t *flp;
+ int i;
+ int j;
+ int x;
+
+ for (i = 0, c = 0, flp = flist; i < FT_nft; i++, flp++) {
+ if (which & (1 << i))
+ c += flp->nfiles;
+ }
+ if (c == 0) {
+ if (flpp)
+ *flpp = NULL;
+ if (fepp)
+ *fepp = NULL;
+ *v = verbose;
+ return 0;
+ }
+ x = (int)(r % c);
+ for (i = 0, c = 0, flp = flist; i < FT_nft; i++, flp++) {
+ if (which & (1 << i)) {
+ if (x < c + flp->nfiles) {
+ fep = &flp->fents[x - c];
+ if (name)
+ fent_to_name(name, flp, fep);
+ if (flpp)
+ *flpp = flp;
+ if (fepp)
+ *fepp = fep;
+ *v = verbose;
+ for (j = 0; !*v && j < ilistlen; j++) {
+ if (ilist[j] == fep->id) {
+ *v = 1;
+ break;
+ }
+ }
+ return 1;
+ }
+ c += flp->nfiles;
+ }
+ }
+#ifdef DEBUG
+ RedPrintf("fsstress: get_fname failure\n");
+ abort();
+#endif
+ return -1;
+
+}
+
+static void init_pathname(pathname_t *name)
+{
+ name->len = 0;
+ name->path = NULL;
+}
+
+static int link_path(pathname_t *name1, pathname_t *name2)
+{
+ char buf1[MAXNAMELEN];
+ char buf2[MAXNAMELEN];
+ int down1;
+ pathname_t newname1;
+ pathname_t newname2;
+ int rval;
+
+ rval = link(name1->path, name2->path);
+ if (rval >= 0 || errno != RED_ENAMETOOLONG)
+ return rval;
+ separate_pathname(name1, buf1, &newname1);
+ separate_pathname(name2, buf2, &newname2);
+ if (strcmp(buf1, buf2) == 0) {
+ if (chdir(buf1) == 0) {
+ rval = link_path(&newname1, &newname2);
+ chdir("..");
+ }
+ } else {
+ if (strcmp(buf1, "..") == 0)
+ down1 = 0;
+ else if (strcmp(buf2, "..") == 0)
+ down1 = 1;
+ else if (strlen(buf1) == 0)
+ down1 = 0;
+ else if (strlen(buf2) == 0)
+ down1 = 1;
+ else
+ down1 = MAX(newname1.len, 3 + name2->len) <=
+ MAX(3 + name1->len, newname2.len);
+ if (down1) {
+ free_pathname(&newname2);
+ append_pathname(&newname2, "../");
+ append_pathname(&newname2, name2->path);
+ if (chdir(buf1) == 0) {
+ rval = link_path(&newname1, &newname2);
+ chdir("..");
+ }
+ } else {
+ free_pathname(&newname1);
+ append_pathname(&newname1, "../");
+ append_pathname(&newname1, name1->path);
+ if (chdir(buf2) == 0) {
+ rval = link_path(&newname1, &newname2);
+ chdir("..");
+ }
+ }
+ }
+ free_pathname(&newname1);
+ free_pathname(&newname2);
+ return rval;
+}
+
+static int lstat64_path(pathname_t *name, REDSTAT *sbuf)
+{
+ char buf[MAXNAMELEN];
+ pathname_t newname;
+ int rval;
+
+ rval = lstat64(name->path, sbuf);
+ if (rval >= 0 || errno != RED_ENAMETOOLONG)
+ return rval;
+ separate_pathname(name, buf, &newname);
+ if (chdir(buf) == 0) {
+ rval = lstat64_path(&newname, sbuf);
+ chdir("..");
+ }
+ free_pathname(&newname);
+ return rval;
+}
+
+static void make_freq_table(void)
+{
+ int f;
+ int i;
+ opdesc_t *p;
+
+ for (p = ops, f = 0; p < ops_end; p++)
+ f += p->freq;
+ freq_table = malloc(f * sizeof(*freq_table));
+ freq_table_size = f;
+ for (p = ops, i = 0; p < ops_end; p++) {
+ for (f = 0; f < p->freq; f++, i++)
+ freq_table[i] = p->op;
+ }
+}
+
+static int mkdir_path(pathname_t *name, mode_t mode)
+{
+ char buf[MAXNAMELEN];
+ pathname_t newname;
+ int rval;
+
+ rval = mkdir(name->path);
+ if (rval >= 0 || errno != RED_ENAMETOOLONG)
+ return rval;
+ separate_pathname(name, buf, &newname);
+ if (chdir(buf) == 0) {
+ rval = mkdir_path(&newname, mode);
+ chdir("..");
+ }
+ free_pathname(&newname);
+ return rval;
+}
+
+static void namerandpad(int id, char *buf, int len)
+{
+ int bucket;
+ static int buckets[8] = {0};
+ static int bucket_count = 0;
+ int bucket_value;
+ int i;
+ int padlen;
+ int padmod;
+
+ if (namerand == 0)
+ return;
+
+ /* buckets[] used to be a statically initialized array with the following
+ initializer: { 2, 4, 8, 16, 32, 64, 128, MAXNAMELEN - 1 }
+
+ The problem is that with Reliance Edge, the maximum name length might be
+ less than 128. So the below code populates buckets[] in a similar
+ fashion but avoids name lengths longer than the maximum. For example,
+ if the max name is 20, the resulting array is { 2, 4, 8, 16, 20 }.
+ */
+ if (!bucket_count) {
+ bucket_count = sizeof(buckets) / sizeof(buckets[0]);
+ bucket_value = 2;
+ for (i = 0; i < bucket_count; i++) {
+ if (bucket_value > 128 || bucket_value >= (int)MAXNAMELEN - 1)
+ break;
+ buckets[i] = bucket_value;
+ bucket_value *= 2;
+ }
+ if (i < bucket_count) {
+ buckets[i] = MAXNAMELEN - 1;
+ i++;
+ }
+ bucket_count = i;
+ }
+
+ bucket = (id ^ namerand) % bucket_count;
+ padmod = buckets[bucket] + 1 - len;
+ if (padmod <= 0)
+ return;
+ padlen = (id ^ namerand) % padmod;
+ if (padlen) {
+ memset(&buf[len], 'X', padlen);
+ buf[len + padlen] = '\0';
+ }
+}
+
+static int open_path(pathname_t *name, int oflag)
+{
+ char buf[MAXNAMELEN];
+ pathname_t newname;
+ int rval;
+
+ rval = open(name->path, oflag);
+ if (rval >= 0 || errno != RED_ENAMETOOLONG)
+ return rval;
+ separate_pathname(name, buf, &newname);
+ if (chdir(buf) == 0) {
+ rval = open_path(&newname, oflag);
+ chdir("..");
+ }
+ free_pathname(&newname);
+ return rval;
+}
+
+static DIR *opendir_path(pathname_t *name)
+{
+ char buf[MAXNAMELEN];
+ pathname_t newname;
+ DIR *rval;
+
+ rval = opendir(name->path);
+ if (rval || errno != RED_ENAMETOOLONG)
+ return rval;
+ separate_pathname(name, buf, &newname);
+ if (chdir(buf) == 0) {
+ rval = opendir_path(&newname);
+ chdir("..");
+ }
+ free_pathname(&newname);
+ return rval;
+}
+
+static int rename_path(pathname_t *name1, pathname_t *name2)
+{
+ char buf1[MAXNAMELEN];
+ char buf2[MAXNAMELEN];
+ int down1;
+ pathname_t newname1;
+ pathname_t newname2;
+ int rval;
+
+ rval = rename(name1->path, name2->path);
+ if (rval >= 0 || errno != RED_ENAMETOOLONG)
+ return rval;
+ separate_pathname(name1, buf1, &newname1);
+ separate_pathname(name2, buf2, &newname2);
+ if (strcmp(buf1, buf2) == 0) {
+ if (chdir(buf1) == 0) {
+ rval = rename_path(&newname1, &newname2);
+ chdir("..");
+ }
+ } else {
+ if (strcmp(buf1, "..") == 0)
+ down1 = 0;
+ else if (strcmp(buf2, "..") == 0)
+ down1 = 1;
+ else if (strlen(buf1) == 0)
+ down1 = 0;
+ else if (strlen(buf2) == 0)
+ down1 = 1;
+ else
+ down1 = MAX(newname1.len, 3 + name2->len) <=
+ MAX(3 + name1->len, newname2.len);
+ if (down1) {
+ free_pathname(&newname2);
+ append_pathname(&newname2, "../");
+ append_pathname(&newname2, name2->path);
+ if (chdir(buf1) == 0) {
+ rval = rename_path(&newname1, &newname2);
+ chdir("..");
+ }
+ } else {
+ free_pathname(&newname1);
+ append_pathname(&newname1, "../");
+ append_pathname(&newname1, name1->path);
+ if (chdir(buf2) == 0) {
+ rval = rename_path(&newname1, &newname2);
+ chdir("..");
+ }
+ }
+ }
+ free_pathname(&newname1);
+ free_pathname(&newname2);
+ return rval;
+}
+
+static int rmdir_path(pathname_t *name)
+{
+ char buf[MAXNAMELEN];
+ pathname_t newname;
+ int rval;
+
+ rval = rmdir(name->path);
+ if (rval >= 0 || errno != RED_ENAMETOOLONG)
+ return rval;
+ separate_pathname(name, buf, &newname);
+ if (chdir(buf) == 0) {
+ rval = rmdir_path(&newname);
+ chdir("..");
+ }
+ free_pathname(&newname);
+ return rval;
+}
+
+static void separate_pathname(pathname_t *name, char *buf, pathname_t *newname)
+{
+ char *slash;
+
+ init_pathname(newname);
+ slash = strchr(name->path, '/');
+ if (slash == NULL) {
+ buf[0] = '\0';
+ return;
+ }
+ *slash = '\0';
+ strcpy(buf, name->path);
+ *slash = '/';
+ append_pathname(newname, slash + 1);
+}
+
+static int stat64_path(pathname_t *name, REDSTAT *sbuf)
+{
+ char buf[MAXNAMELEN];
+ pathname_t newname;
+ int rval;
+
+ rval = stat64(name->path, sbuf);
+ if (rval >= 0 || errno != RED_ENAMETOOLONG)
+ return rval;
+ separate_pathname(name, buf, &newname);
+ if (chdir(buf) == 0) {
+ rval = stat64_path(&newname, sbuf);
+ chdir("..");
+ }
+ free_pathname(&newname);
+ return rval;
+}
+
+static int truncate64_path(pathname_t *name, off64_t length)
+{
+ char buf[MAXNAMELEN];
+ pathname_t newname;
+ int rval;
+
+ rval = truncate64(name->path, length);
+ if (rval >= 0 || errno != RED_ENAMETOOLONG)
+ return rval;
+ separate_pathname(name, buf, &newname);
+ if (chdir(buf) == 0) {
+ rval = truncate64_path(&newname, length);
+ chdir("..");
+ }
+ free_pathname(&newname);
+ return rval;
+}
+
+static int unlink_path(pathname_t *name)
+{
+ char buf[MAXNAMELEN];
+ pathname_t newname;
+ int rval;
+
+ rval = unlink(name->path);
+ if (rval >= 0 || errno != RED_ENAMETOOLONG)
+ return rval;
+ separate_pathname(name, buf, &newname);
+ if (chdir(buf) == 0) {
+ rval = unlink_path(&newname);
+ chdir("..");
+ }
+ free_pathname(&newname);
+ return rval;
+}
+
+static void usage(const char *progname)
+{
+ RedPrintf("usage: %s VolumeID [Options]\n", progname);
+ RedPrintf("File system stress test.\n\n");
+ RedPrintf("Where:\n");
+ RedPrintf(" VolumeID\n");
+ RedPrintf(" A volume number (e.g., 2) or a volume path prefix (e.g., VOL1: or /data)\n");
+ RedPrintf(" of the volume to test.\n");
+ RedPrintf("And 'Options' are any of the following:\n");
+ RedPrintf(" --no-cleanup, -c\n");
+ RedPrintf(" Specifies not to remove files (cleanup) after execution\n");
+ RedPrintf(" --loops=count, -l count\n");
+ RedPrintf(" Specifies the number of times the entire test should loop. Use 0 for\n");
+ RedPrintf(" infinite. Default 1.\n");
+ RedPrintf(" --nops=count, -n count\n");
+ RedPrintf(" Specifies the number of operations to run (default 10000).\n");
+ RedPrintf(" --namepad, -r\n");
+ RedPrintf(" Specifies to use random name padding (resulting in longer names).\n");
+ RedPrintf(" --seed=value, -s value\n");
+ RedPrintf(" Specifies the seed for the random number generator (default timestamp).\n");
+ RedPrintf(" --verbose, -v\n");
+ RedPrintf(" Specifies verbose mode (without this, test is very quiet).\n");
+ RedPrintf(" --dev=devname, -D devname\n");
+ RedPrintf(" Specifies the device name. This is typically only meaningful when\n");
+ RedPrintf(" running the test on a host machine. This can be \"ram\" to test on a RAM\n");
+ RedPrintf(" disk, the path and name of a file disk (e.g., red.bin); or an OS-specific\n");
+ RedPrintf(" reference to a device (on Windows, a drive letter like G: or a device name\n");
+ RedPrintf(" like \\\\.\\PhysicalDrive7).\n");
+ RedPrintf(" --help, -H\n");
+ RedPrintf(" Prints this usage text and exits.\n\n");
+ RedPrintf("Warning: This test will format the volume -- destroying all existing data.\n\n");
+}
+
+static void creat_f(int opno, long r)
+{
+ int e;
+ int e1;
+ pathname_t f;
+ int fd;
+ fent_t *fep;
+ int id;
+ int parid;
+ int type;
+ int v;
+ int v1;
+ int esz = 0;
+
+ if (!get_fname(FT_DIRm, r, NULL, NULL, &fep, &v1))
+ parid = -1;
+ else
+ parid = fep->id;
+ init_pathname(&f);
+ type = rtpct ? ((int)(random() % 100) > rtpct ? FT_REG : FT_RTF) : FT_REG;
+ e = generate_fname(fep, type, &f, &id, &v);
+ v |= v1;
+ if (!e) {
+ if (v) {
+ fent_to_name(&f, &flist[FT_DIR], fep);
+ RedPrintf("%d/%d: creat - no filename from %s\n",
+ procid, opno, f.path);
+ }
+ free_pathname(&f);
+ return;
+ }
+ fd = creat_path(&f, 0666);
+ e = fd < 0 ? errno : 0;
+ e1 = 0;
+ check_cwd();
+ esz = 0;
+ if (fd >= 0) {
+ add_to_flist(type, id, parid);
+ close(fd);
+ }
+ if (v)
+ RedPrintf("%d/%d: creat %s x:%d %d %d\n", procid, opno, f.path,
+ esz, e, e1);
+ free_pathname(&f);
+}
+
+static void fdatasync_f(int opno, long r)
+{
+ int e;
+ pathname_t f;
+ int fd;
+ int v;
+
+ init_pathname(&f);
+ if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {
+ if (v)
+ RedPrintf("%d/%d: fdatasync - no filename\n",
+ procid, opno);
+ free_pathname(&f);
+ return;
+ }
+ fd = open_path(&f, O_WRONLY);
+ e = fd < 0 ? errno : 0;
+ check_cwd();
+ if (fd < 0) {
+ if (v)
+ RedPrintf("%d/%d: fdatasync - open %s failed %d\n",
+ procid, opno, f.path, e);
+ free_pathname(&f);
+ return;
+ }
+ e = fdatasync(fd) < 0 ? errno : 0;
+ if (v)
+ RedPrintf("%d/%d: fdatasync %s %d\n", procid, opno, f.path, e);
+ free_pathname(&f);
+ close(fd);
+}
+
+static void fsync_f(int opno, long r)
+{
+ int e;
+ pathname_t f;
+ int fd;
+ int v;
+
+ init_pathname(&f);
+ if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {
+ if (v)
+ RedPrintf("%d/%d: fsync - no filename\n", procid, opno);
+ free_pathname(&f);
+ return;
+ }
+ fd = open_path(&f, O_WRONLY);
+ e = fd < 0 ? errno : 0;
+ check_cwd();
+ if (fd < 0) {
+ if (v)
+ RedPrintf("%d/%d: fsync - open %s failed %d\n",
+ procid, opno, f.path, e);
+ free_pathname(&f);
+ return;
+ }
+ e = fsync(fd) < 0 ? errno : 0;
+ if (v)
+ RedPrintf("%d/%d: fsync %s %d\n", procid, opno, f.path, e);
+ free_pathname(&f);
+ close(fd);
+}
+
+static void getdents_f(int opno, long r)
+{
+ DIR *dir;
+ pathname_t f;
+ int v;
+
+ init_pathname(&f);
+ if (!get_fname(FT_DIRm, r, &f, NULL, NULL, &v))
+ append_pathname(&f, ".");
+ dir = opendir_path(&f);
+ check_cwd();
+ if (dir == NULL) {
+ if (v)
+ RedPrintf("%d/%d: getdents - can't open %s\n",
+ procid, opno, f.path);
+ free_pathname(&f);
+ return;
+ }
+ while (readdir64(dir) != NULL)
+ continue;
+ if (v)
+ RedPrintf("%d/%d: getdents %s 0\n", procid, opno, f.path);
+ free_pathname(&f);
+ closedir(dir);
+}
+
+static void link_f(int opno, long r)
+{
+ int e;
+ pathname_t f;
+ fent_t *fep;
+ flist_t *flp;
+ int id;
+ pathname_t l;
+ int parid;
+ int v;
+ int v1;
+
+ init_pathname(&f);
+ if (!get_fname(FT_NOTDIR, r, &f, &flp, NULL, &v1)) {
+ if (v1)
+ RedPrintf("%d/%d: link - no file\n", procid, opno);
+ free_pathname(&f);
+ return;
+ }
+ if (!get_fname(FT_DIRm, random(), NULL, NULL, &fep, &v))
+ parid = -1;
+ else
+ parid = fep->id;
+ v |= v1;
+ init_pathname(&l);
+ e = generate_fname(fep, (int)(flp - flist), &l, &id, &v1);
+ v |= v1;
+ if (!e) {
+ if (v) {
+ fent_to_name(&l, &flist[FT_DIR], fep);
+ RedPrintf("%d/%d: link - no filename from %s\n",
+ procid, opno, l.path);
+ }
+ free_pathname(&l);
+ free_pathname(&f);
+ return;
+ }
+ e = link_path(&f, &l) < 0 ? errno : 0;
+ check_cwd();
+ if (e == 0)
+ add_to_flist((int)(flp - flist), id, parid);
+ if (v)
+ RedPrintf("%d/%d: link %s %s %d\n", procid, opno, f.path, l.path,
+ e);
+ free_pathname(&l);
+ free_pathname(&f);
+}
+
+static void mkdir_f(int opno, long r)
+{
+ int e;
+ pathname_t f;
+ fent_t *fep;
+ int id;
+ int parid;
+ int v;
+ int v1;
+
+ if (!get_fname(FT_DIRm, r, NULL, NULL, &fep, &v))
+ parid = -1;
+ else
+ parid = fep->id;
+ init_pathname(&f);
+ e = generate_fname(fep, FT_DIR, &f, &id, &v1);
+ v |= v1;
+ if (!e) {
+ if (v) {
+ fent_to_name(&f, &flist[FT_DIR], fep);
+ RedPrintf("%d/%d: mkdir - no filename from %s\n",
+ procid, opno, f.path);
+ }
+ free_pathname(&f);
+ return;
+ }
+ e = mkdir_path(&f, 0777) < 0 ? errno : 0;
+ check_cwd();
+ if (e == 0)
+ add_to_flist(FT_DIR, id, parid);
+ if (v)
+ RedPrintf("%d/%d: mkdir %s %d\n", procid, opno, f.path, e);
+ free_pathname(&f);
+}
+
+static void read_f(int opno, long r)
+{
+ char *buf;
+ int e;
+ pathname_t f;
+ int fd;
+ uint32_t len;
+ __int64_t lr;
+ off64_t off;
+ REDSTAT stb;
+ int v;
+
+ init_pathname(&f);
+ if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {
+ if (v)
+ RedPrintf("%d/%d: read - no filename\n", procid, opno);
+ free_pathname(&f);
+ return;
+ }
+ fd = open_path(&f, O_RDONLY);
+ e = fd < 0 ? errno : 0;
+ check_cwd();
+ if (fd < 0) {
+ if (v)
+ RedPrintf("%d/%d: read - open %s failed %d\n",
+ procid, opno, f.path, e);
+ free_pathname(&f);
+ return;
+ }
+ if (fstat64(fd, &stb) < 0) {
+ if (v)
+ RedPrintf("%d/%d: read - fstat64 %s failed %d\n",
+ procid, opno, f.path, errno);
+ free_pathname(&f);
+ close(fd);
+ return;
+ }
+ if (stb.st_size == 0) {
+ if (v)
+ RedPrintf("%d/%d: read - %s zero size\n", procid, opno,
+ f.path);
+ free_pathname(&f);
+ close(fd);
+ return;
+ }
+ lr = ((__int64_t) random() << 32) + random();
+ off = (off64_t) (lr % stb.st_size);
+ lseek64(fd, off, SEEK_SET);
+ len = (random() % (getpagesize() * 4)) + 1;
+ buf = malloc(len);
+ e = read(fd, buf, len) < 0 ? errno : 0;
+ free(buf);
+ if (v)
+ RedPrintf("%d/%d: read %s [%lld,%ld] %d\n",
+ procid, opno, f.path, (long long)off, (long int)len, e);
+ free_pathname(&f);
+ close(fd);
+}
+
+static void rename_f(int opno, long r)
+{
+ fent_t *dfep;
+ int e;
+ pathname_t f;
+ fent_t *fep;
+ flist_t *flp;
+ int id;
+ pathname_t newf;
+ int oldid;
+ int parid;
+ int v;
+ int v1;
+
+ init_pathname(&f);
+ if (!get_fname(FT_ANYm, r, &f, &flp, &fep, &v1)) {
+ if (v1)
+ RedPrintf("%d/%d: rename - no filename\n", procid, opno);
+ free_pathname(&f);
+ return;
+ }
+ if (!get_fname(FT_DIRm, random(), NULL, NULL, &dfep, &v))
+ parid = -1;
+ else
+ parid = dfep->id;
+ v |= v1;
+ init_pathname(&newf);
+ e = generate_fname(dfep, (int)(flp - flist), &newf, &id, &v1);
+ v |= v1;
+ if (!e) {
+ if (v) {
+ fent_to_name(&f, &flist[FT_DIR], dfep);
+ RedPrintf("%d/%d: rename - no filename from %s\n",
+ procid, opno, f.path);
+ }
+ free_pathname(&newf);
+ free_pathname(&f);
+ return;
+ }
+ e = rename_path(&f, &newf) < 0 ? errno : 0;
+ check_cwd();
+ if (e == 0) {
+ if (flp - flist == FT_DIR) {
+ oldid = fep->id;
+ fix_parent(oldid, id);
+ }
+ del_from_flist((int)(flp - flist), (int)(fep - flp->fents));
+ add_to_flist((int)(flp - flist), id, parid);
+ }
+ if (v)
+ RedPrintf("%d/%d: rename %s to %s %d\n", procid, opno, f.path,
+ newf.path, e);
+ free_pathname(&newf);
+ free_pathname(&f);
+}
+
+static void rmdir_f(int opno, long r)
+{
+ int e;
+ pathname_t f;
+ fent_t *fep;
+ int v;
+
+ init_pathname(&f);
+ if (!get_fname(FT_DIRm, r, &f, NULL, &fep, &v)) {
+ if (v)
+ RedPrintf("%d/%d: rmdir - no directory\n", procid, opno);
+ free_pathname(&f);
+ return;
+ }
+ e = rmdir_path(&f) < 0 ? errno : 0;
+ check_cwd();
+ if (e == 0)
+ del_from_flist(FT_DIR, (int)(fep - flist[FT_DIR].fents));
+ if (v)
+ RedPrintf("%d/%d: rmdir %s %d\n", procid, opno, f.path, e);
+ free_pathname(&f);
+}
+
+static void stat_f(int opno, long r)
+{
+ int e;
+ pathname_t f;
+ REDSTAT stb;
+ int v;
+
+ init_pathname(&f);
+ if (!get_fname(FT_ANYm, r, &f, NULL, NULL, &v)) {
+ if (v)
+ RedPrintf("%d/%d: stat - no entries\n", procid, opno);
+ free_pathname(&f);
+ return;
+ }
+ e = lstat64_path(&f, &stb) < 0 ? errno : 0;
+ check_cwd();
+ if (v)
+ RedPrintf("%d/%d: stat %s %d\n", procid, opno, f.path, e);
+ free_pathname(&f);
+}
+
+static void truncate_f(int opno, long r)
+{
+ int e;
+ pathname_t f;
+ __int64_t lr;
+ off64_t off;
+ REDSTAT stb;
+ int v;
+
+ init_pathname(&f);
+ if (!get_fname(FT_REGFILE, r, &f, NULL, NULL, &v)) {
+ if (v)
+ RedPrintf("%d/%d: truncate - no filename\n", procid, opno);
+ free_pathname(&f);
+ return;
+ }
+ e = stat64_path(&f, &stb) < 0 ? errno : 0;
+ check_cwd();
+ if (e > 0) {
+ if (v)
+ RedPrintf("%d/%d: truncate - stat64 %s failed %d\n",
+ procid, opno, f.path, e);
+ free_pathname(&f);
+ return;
+ }
+ lr = ((__int64_t) random() << 32) + random();
+ off = lr % MIN(stb.st_size + (1024 * 1024), MAXFSIZE);
+ off %= maxfsize;
+ e = truncate64_path(&f, off) < 0 ? errno : 0;
+ check_cwd();
+ if (v)
+ RedPrintf("%d/%d: truncate %s %lld %d\n", procid, opno, f.path,
+ (long long)off, e);
+ free_pathname(&f);
+}
+
+static void unlink_f(int opno, long r)
+{
+ int e;
+ pathname_t f;
+ fent_t *fep;
+ flist_t *flp;
+ int v;
+
+ init_pathname(&f);
+ if (!get_fname(FT_NOTDIR, r, &f, &flp, &fep, &v)) {
+ if (v)
+ RedPrintf("%d/%d: unlink - no file\n", procid, opno);
+ free_pathname(&f);
+ return;
+ }
+ e = unlink_path(&f) < 0 ? errno : 0;
+ check_cwd();
+ if (e == 0)
+ del_from_flist((int)(flp - flist), (int)(fep - flp->fents));
+ if (v)
+ RedPrintf("%d/%d: unlink %s %d\n", procid, opno, f.path, e);
+ free_pathname(&f);
+}
+
+static void write_f(int opno, long r)
+{
+ char *buf;
+ int e;
+ pathname_t f;
+ int fd;
+ uint32_t len;
+ __int64_t lr;
+ off64_t off;
+ REDSTAT stb;
+ int v;
+
+ init_pathname(&f);
+ if (!get_fname(FT_REGm, r, &f, NULL, NULL, &v)) {
+ if (v)
+ RedPrintf("%d/%d: write - no filename\n", procid, opno);
+ free_pathname(&f);
+ return;
+ }
+ fd = open_path(&f, O_WRONLY);
+ e = fd < 0 ? errno : 0;
+ check_cwd();
+ if (fd < 0) {
+ if (v)
+ RedPrintf("%d/%d: write - open %s failed %d\n",
+ procid, opno, f.path, e);
+ free_pathname(&f);
+ return;
+ }
+ if (fstat64(fd, &stb) < 0) {
+ if (v)
+ RedPrintf("%d/%d: write - fstat64 %s failed %d\n",
+ procid, opno, f.path, errno);
+ free_pathname(&f);
+ close(fd);
+ return;
+ }
+ lr = ((__int64_t) random() << 32) + random();
+ off = (off64_t) (lr % MIN(stb.st_size + (1024 * 1024), MAXFSIZE));
+ off %= maxfsize;
+ lseek64(fd, off, SEEK_SET);
+ len = (random() % (getpagesize() * 4)) + 1;
+ buf = malloc(len);
+ memset(buf, nameseq & 0xff, len);
+ e = write(fd, buf, len) < 0 ? errno : 0;
+ free(buf);
+ if (v)
+ RedPrintf("%d/%d: write %s [%lld,%ld] %d\n",
+ procid, opno, f.path, (long long)off, (long int)len, e);
+ free_pathname(&f);
+ close(fd);
+}
+
+
+#if REDCONF_CHECKER == 1
+static void check_f(int opno, long r)
+{
+ int32_t ret;
+ const char *pszVolume = gpRedVolConf->pszPathPrefix;
+
+ (void)r;
+
+ errno = 0;
+
+ ret = red_transact(pszVolume);
+
+ if(ret == 0)
+ {
+ ret = red_umount(pszVolume);
+
+ if(ret == 0)
+ {
+ int32_t ret2;
+
+ errno = -RedCoreVolCheck();
+ if(errno != 0)
+ {
+ ret = -1;
+ }
+
+ ret2 = red_mount(pszVolume);
+
+ if(ret == 0)
+ {
+ ret = ret2;
+ }
+
+ if(ret2 != 0)
+ {
+ exit(1);
+ }
+ }
+ }
+
+ if (verbose)
+ {
+ RedPrintf("%d/%d: check %s %d\n", procid, opno, pszVolume, errno);
+ }
+}
+#endif
+
+
+#endif /* FSSTRESS_SUPPORTED */
+
-/* ----> DO NOT REMOVE THE FOLLOWING NOTICE <----\r
-\r
- Copyright (c) 2014-2015 Datalight, Inc.\r
- All Rights Reserved Worldwide.\r
-\r
- This program is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; use version 2 of the License.\r
-\r
- This program is distributed in the hope that it will be useful,\r
- but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty\r
- of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License along\r
- with this program; if not, write to the Free Software Foundation, Inc.,\r
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
-*/\r
-/* Businesses and individuals that for commercial or other reasons cannot\r
- comply with the terms of the GPLv2 license may obtain a commercial license\r
- before incorporating Reliance Edge into proprietary software for\r
- distribution in any form. Visit http://www.datalight.com/reliance-edge for\r
- more information.\r
-*/\r
-/** @file\r
- @brief Implements utilities that convert strings to numbers.\r
-*/\r
-#include <redfs.h>\r
-#include <redtestutils.h>\r
-\r
-\r
-#define ISHEXDIGITU(c) (((c) >= 'A') && ((c) <= 'F'))\r
-#define ISHEXDIGITL(c) (((c) >= 'a') && ((c) <= 'f'))\r
-#define ISHEXDIGIT(c) (ISHEXDIGITL(c) || ISHEXDIGITU(c))\r
-\r
-\r
-/** @brief Converts an ASCII number into an int32_t.\r
-\r
- Converts all decimal digit numbers up to the end of the string or to the\r
- first non-numerical character.\r
-\r
- @note This function does *not* ignore leading white space.\r
-\r
- @param pszNum Pointer to a constant array of characters.\r
-\r
- @return The integer represented in the string.\r
-*/\r
-int32_t RedAtoI(\r
- const char *pszNum)\r
-{\r
- int32_t lValue = 0;\r
- int32_t lNegative = 1;\r
- uint32_t ulIdx = 0U;\r
-\r
- if(pszNum[ulIdx] == '+')\r
- {\r
- ulIdx++;\r
- }\r
- else if(pszNum[ulIdx] == '-')\r
- {\r
- ulIdx++;\r
- lNegative = -1;\r
- }\r
- else\r
- {\r
- /* No sign, implicitly positive.\r
- */\r
- }\r
-\r
- while(ISDIGIT(pszNum[ulIdx]))\r
- {\r
- lValue *= 10;\r
- lValue += pszNum[ulIdx] - '0';\r
- ulIdx++;\r
- }\r
-\r
- lValue *= lNegative;\r
-\r
- return lValue;\r
-}\r
-\r
-\r
-/** @brief Convert a hexadecimal ASCII number into a uint32_t value.\r
-\r
- The function processes all hex digits up to a NUL-terminator, or to the\r
- first non-hex character. Only hexadecimal digits are processed, so leading\r
- white space, or a leading "0x" prefix are not allowed.\r
-\r
- If pachNum points to an empty string (points to a NUL), this function will\r
- return NULL, and the value at *pulNum will not be modified.\r
-\r
- @note This function does not check for overflow. If there are more\r
- significant digits than can be represented in a uint32_t variable, the\r
- output is unspecified.\r
-\r
- @param pachNum A pointer to a constant array of hex characters.\r
- @param pulNum A pointer to the location in which to store the uint32_t\r
- result. Upon return, this value will be modified ONLY if\r
- the function succeeds and the returned pointer is valid (not\r
- NULL).\r
-\r
- @return A pointer to the byte following the converted number or NULL to\r
- indicate failure.\r
-*/\r
-const char *RedHtoUL(\r
- const char *pszNum,\r
- uint32_t *pulNum)\r
-{\r
- uint64_t ullValue;\r
- const char *pszReturn;\r
-\r
- pszReturn = RedHtoULL(pszNum, &ullValue);\r
- if(pszReturn != NULL)\r
- {\r
- if(ullValue < UINT32_MAX)\r
- {\r
- *pulNum = (uint32_t)ullValue;\r
- }\r
- else\r
- {\r
- pszReturn = NULL;\r
- }\r
- }\r
-\r
- return pszReturn;\r
-}\r
-\r
-\r
-/** @brief Convert a hexadecimal ASCII number into a D_UINT64 value.\r
-\r
- The function processes all hex digits up to a NUL-terminator, or to the\r
- first non-hex character. Only hexadecimal digits are processed, so leading\r
- white space, or a leading "0x" prefix are not allowed.\r
-\r
- If pachNum points to an empty string (points to a NUL), this function will\r
- return NULL, and the value at *pulNum will not be modified.\r
-\r
- @note This function does not check for overflow. If there are more\r
- significant digits than can be represented in a uint64_t variable, the\r
- output is unspecified.\r
-\r
- @param pszNum A pointer to a constant array of hex characters.\r
- @param pullNum A pointer to the location in which to store the uint64_t\r
- result. Upon return, this value will be modified ONLY if\r
- the function succeeds and the returned pointer is valid (not\r
- NULL).\r
-\r
- @return A pointer to the byte following the converted number, or NULL to\r
- indicate failure.\r
-*/\r
-const char *RedHtoULL(\r
- const char *pszNum,\r
- uint64_t *pullNum)\r
-{\r
- uint64_t ullValue = 0U;\r
- const char *pszReturn = NULL;\r
- uint32_t ulIdx = 0U;\r
-\r
- REDASSERT(pszNum != NULL);\r
- REDASSERT(pullNum != NULL);\r
-\r
- while(pszNum[ulIdx] != '\0')\r
- {\r
- char cDigit = pszNum[ulIdx];\r
-\r
- if(ISDIGIT(cDigit))\r
- {\r
- cDigit -= '0';\r
- }\r
- else if(ISHEXDIGITU(cDigit))\r
- {\r
- cDigit -= ('A' - 10);\r
- }\r
- else if(ISHEXDIGITL(cDigit))\r
- {\r
- cDigit -= ('a' - 10);\r
- }\r
- else\r
- {\r
- break;\r
- }\r
-\r
- REDASSERT((ullValue & UINT64_SUFFIX(0xF000000000000000)) == 0U);\r
-\r
- ullValue <<= 4U;\r
- ullValue += cDigit;\r
-\r
- ulIdx++;\r
- pszReturn = &pszNum[ulIdx];\r
- }\r
-\r
- /* Modify the number returned only if we found one or more valid hex\r
- digits.\r
- */\r
- if(pszReturn != NULL)\r
- {\r
- *pullNum = ullValue;\r
- }\r
-\r
- return pszReturn;\r
-}\r
-\r
-\r
-/** @brief Convert the ASCII number to a uint32_t value.\r
-\r
- The number may be hex or decimal. Hex numbers must be prefixed by '0x', and\r
- they may be upper or lower case. The conversion process will stop with the\r
- first non hex or decimal digit.\r
-\r
- If the number is negative (the first character is a '-' sign), the value\r
- will be range checked and returned as the equivalent unsigned value.\r
-\r
- @note This function will NOT fail for numbers which exceed the size of a\r
- uint32_t value.\r
-\r
- @param pszNum A pointer to the ASCII number to convert\r
- @param pulNum A pointer to the uint32_t location to store the result.\r
- This value will be modified on return only if the function\r
- succeeds and the returned pointer is valid (not NULL).\r
-\r
- @return A pointer to the byte following the converted number, or NULL to\r
- indicate failure.\r
-*/\r
-const char *RedNtoUL(\r
- const char *pszNum,\r
- uint32_t *pulNum)\r
-{\r
- bool fNegative = false;\r
- uint32_t ulIdx = 0U;\r
- const char *pszReturn;\r
-\r
- REDASSERT(pszNum != NULL);\r
- REDASSERT(pulNum != NULL);\r
-\r
- if(pszNum[ulIdx] == '-')\r
- {\r
- fNegative = true;\r
- ulIdx++;\r
- }\r
-\r
- /* Hex numbers must be prefixed with '0x'.\r
- */\r
- if((pszNum[ulIdx] == '0') && ((pszNum[ulIdx + 1U] == 'x') || (pszNum[ulIdx + 1U] == 'X')))\r
- {\r
- ulIdx += 2U;\r
-\r
- if(ISDIGIT(pszNum[ulIdx]) || ISHEXDIGIT(pszNum[ulIdx]))\r
- {\r
- pszReturn = RedHtoUL(&pszNum[ulIdx], pulNum);\r
- }\r
- else\r
- {\r
- pszReturn = NULL;\r
- }\r
- }\r
- else if(ISDIGIT(pszNum[ulIdx]))\r
- {\r
- uint32_t ulTemp;\r
-\r
- ulTemp = RedAtoI(&pszNum[ulIdx]);\r
-\r
- while(ISDIGIT(pszNum[ulIdx]))\r
- {\r
- ulIdx++;\r
- }\r
-\r
- if(fNegative)\r
- {\r
- /* Fail if the number is out of range.\r
- */\r
- if(ulTemp > INT32_MAX)\r
- {\r
- pszReturn = NULL;\r
- }\r
- else\r
- {\r
- *pulNum = -((int32_t)ulTemp);\r
- pszReturn = &pszNum[ulIdx];\r
- }\r
- }\r
- else\r
- {\r
- *pulNum = ulTemp;\r
- pszReturn = &pszNum[ulIdx];\r
- }\r
- }\r
- else\r
- {\r
- /* Return an error if there is not at least one hex or decimal digit.\r
- */\r
- pszReturn = NULL;\r
- }\r
-\r
- return pszReturn;\r
-}\r
-\r
-\r
-/** @brief Convert the ASCII number pointed to by pachNum to a uint64_t value.\r
-\r
- The number may be hex or decimal. Hex numbers must be prefixed by '0x', and\r
- they may be upper or lower case. The conversion process will stop with the\r
- first non hex or decimal digit.\r
-\r
- If the number is negative (the first character is a '-' sign), the value\r
- will be range checked and returned as the equivalent unsigned value.\r
-\r
- @param pszNum A pointer to the ASCII number to convert.\r
- @param pullNum A pointer to the uint64_t location to store the result.\r
- This value will be modified on return only if the function\r
- succeeds and the returned pointer is valid (not NULL).\r
-\r
- @return A pointer to the byte following the converted number, or NULL to\r
- indicate failure.\r
-*/\r
-const char *RedNtoULL(\r
- const char *pszNum,\r
- uint64_t *pullNum)\r
-{\r
- bool fNegative = false;\r
- uint32_t ulIdx = 0U;\r
- const char *pszReturn;\r
-\r
- REDASSERT(pszNum != NULL);\r
- REDASSERT(pullNum != NULL);\r
-\r
- if(pszNum[ulIdx] == '-')\r
- {\r
- fNegative = true;\r
- ulIdx++;\r
- }\r
-\r
- /* Hex numbers must be prefixed with '0x'.\r
- */\r
- if((pszNum[ulIdx] == '0') && ((pszNum[ulIdx + 1U] == 'x') || (pszNum[ulIdx + 1U] == 'X')))\r
- {\r
- ulIdx += 2U;\r
-\r
- if(ISDIGIT(pszNum[ulIdx]) || ISHEXDIGIT(pszNum[ulIdx]))\r
- {\r
- pszReturn = RedHtoULL(&pszNum[ulIdx], pullNum);\r
- }\r
- else\r
- {\r
- pszReturn = NULL;\r
- }\r
- }\r
- else if(ISDIGIT(pszNum[ulIdx]))\r
- {\r
- uint64_t ullTemp = 0U;\r
-\r
- while(ISDIGIT(pszNum[ulIdx]))\r
- {\r
- ullTemp *= 10U;\r
- ullTemp += pszNum[ulIdx] - '0';\r
- ulIdx++;\r
- }\r
-\r
- if(fNegative)\r
- {\r
- /* Fail if the number is out of range.\r
- */\r
- if(ullTemp > INT64_MAX)\r
- {\r
- pszReturn = NULL;\r
- }\r
- else\r
- {\r
- *pullNum = (uint64_t)(-((int64_t)ullTemp));\r
- pszReturn = &pszNum[ulIdx];\r
- }\r
- }\r
- else\r
- {\r
- *pullNum = ullTemp;\r
- pszReturn = &pszNum[ulIdx];\r
- }\r
- }\r
- else\r
- {\r
- /* Return an error if there is not at least one hex or decimal digit.\r
- */\r
- pszReturn = NULL;\r
- }\r
-\r
- return pszReturn;\r
-}\r
-\r
-\r
-/** @brief Convert an ASCII hex or decimal number, which may may have a "B",\r
- "KB", or "MB" suffix (case insensitive), to a binary value.\r
-\r
- Hex numbers must be prefixed with "0x".\r
-\r
- @note If there is no postfix, KB is assumed!\r
-\r
- May fail due to bad formatting or overflow.\r
-\r
- @param pszNum A pointer to the ASCII number to convert.\r
- @param pulResult A pointer to a uint32_t in which to place the result.\r
-\r
- @return A pointer to the byte following the string, or NULL to indicate an\r
- error. In the event of an error, *pulResult will not be modified.\r
-*/\r
-const char *RedSizeToUL(\r
- const char *pszNum,\r
- uint32_t *pulResult)\r
-{\r
- uint32_t ulResult;\r
- const char *pszSuffix;\r
- const char *pszReturn;\r
- uint32_t ulIdx = 0U;\r
-\r
- REDASSERT(pszNum != NULL);\r
- REDASSERT(pulResult != NULL);\r
-\r
- /* Do the basic hex/decimal conversion\r
- */\r
- pszSuffix = RedNtoUL(pszNum, &ulResult);\r
- if(pszSuffix != NULL)\r
- {\r
- if((pszSuffix[ulIdx] == 'B') || (pszSuffix[ulIdx] == 'b'))\r
- {\r
- ulIdx++;\r
- pszReturn = &pszSuffix[ulIdx];\r
- }\r
- else if( ((pszSuffix[ulIdx] == 'M') || (pszSuffix[ulIdx] == 'm'))\r
- && ((pszSuffix[ulIdx + 1U] == 'B') || (pszSuffix[ulIdx + 1U] == 'b')))\r
- {\r
- ulIdx += 2U;\r
-\r
- if(ulResult > (UINT32_MAX / (1024U * 1024U)))\r
- {\r
- pszReturn = NULL;\r
- }\r
- else\r
- {\r
- ulResult *= 1024U * 1024U;\r
- pszReturn = &pszSuffix[ulIdx];\r
- }\r
- }\r
- else\r
- {\r
- /* The number is either postfixed with "KB" or something\r
- else (we don't care), but we must increment the pointer\r
- if it is something recognize.\r
- */\r
- if( ((pszSuffix[ulIdx] == 'K') || (pszSuffix[ulIdx] == 'k'))\r
- && ((pszSuffix[ulIdx + 1U] == 'B') || (pszSuffix[ulIdx + 1U] == 'b')))\r
- {\r
- ulIdx += 2U;\r
- }\r
-\r
- /* "B" or "MB" were not specified, so it must be "KB"\r
- */\r
- if(ulResult > (UINT32_MAX / 1024U))\r
- {\r
- pszReturn = NULL;\r
- }\r
- else\r
- {\r
- ulResult *= 1024UL;\r
- pszReturn = &pszSuffix[ulIdx];\r
- }\r
- }\r
-\r
- if(pszReturn != NULL)\r
- {\r
- *pulResult = ulResult;\r
- }\r
- }\r
- else\r
- {\r
- pszReturn = NULL;\r
- }\r
-\r
- return pszReturn;\r
-}\r
-\r
+/* ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
+
+ Copyright (c) 2014-2015 Datalight, Inc.
+ All Rights Reserved Worldwide.
+
+ 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; use version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but "AS-IS," 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+/* Businesses and individuals that for commercial or other reasons cannot
+ comply with the terms of the GPLv2 license may obtain a commercial license
+ before incorporating Reliance Edge into proprietary software for
+ distribution in any form. Visit http://www.datalight.com/reliance-edge for
+ more information.
+*/
+/** @file
+ @brief Implements utilities that convert strings to numbers.
+*/
+#include <redfs.h>
+#include <redtestutils.h>
+
+
+#define ISHEXDIGITU(c) (((c) >= 'A') && ((c) <= 'F'))
+#define ISHEXDIGITL(c) (((c) >= 'a') && ((c) <= 'f'))
+#define ISHEXDIGIT(c) (ISHEXDIGITL(c) || ISHEXDIGITU(c))
+
+
+/** @brief Converts an ASCII number into an int32_t.
+
+ Converts all decimal digit numbers up to the end of the string or to the
+ first non-numerical character.
+
+ @note This function does *not* ignore leading white space.
+
+ @param pszNum Pointer to a constant array of characters.
+
+ @return The integer represented in the string.
+*/
+int32_t RedAtoI(
+ const char *pszNum)
+{
+ int32_t lValue = 0;
+ int32_t lNegative = 1;
+ uint32_t ulIdx = 0U;
+
+ if(pszNum[ulIdx] == '+')
+ {
+ ulIdx++;
+ }
+ else if(pszNum[ulIdx] == '-')
+ {
+ ulIdx++;
+ lNegative = -1;
+ }
+ else
+ {
+ /* No sign, implicitly positive.
+ */
+ }
+
+ while(ISDIGIT(pszNum[ulIdx]))
+ {
+ lValue *= 10;
+ lValue += pszNum[ulIdx] - '0';
+ ulIdx++;
+ }
+
+ lValue *= lNegative;
+
+ return lValue;
+}
+
+
+/** @brief Convert a hexadecimal ASCII number into a uint32_t value.
+
+ The function processes all hex digits up to a NUL-terminator, or to the
+ first non-hex character. Only hexadecimal digits are processed, so leading
+ white space, or a leading "0x" prefix are not allowed.
+
+ If pachNum points to an empty string (points to a NUL), this function will
+ return NULL, and the value at *pulNum will not be modified.
+
+ @note This function does not check for overflow. If there are more
+ significant digits than can be represented in a uint32_t variable, the
+ output is unspecified.
+
+ @param pszNum A pointer to a constant array of hex characters.
+ @param pulNum A pointer to the location in which to store the uint32_t
+ result. Upon return, this value will be modified ONLY if
+ the function succeeds and the returned pointer is valid (not
+ NULL).
+
+ @return A pointer to the byte following the converted number or NULL to
+ indicate failure.
+*/
+const char *RedHtoUL(
+ const char *pszNum,
+ uint32_t *pulNum)
+{
+ uint64_t ullValue;
+ const char *pszReturn;
+
+ pszReturn = RedHtoULL(pszNum, &ullValue);
+ if(pszReturn != NULL)
+ {
+ if(ullValue < UINT32_MAX)
+ {
+ *pulNum = (uint32_t)ullValue;
+ }
+ else
+ {
+ pszReturn = NULL;
+ }
+ }
+
+ return pszReturn;
+}
+
+
+/** @brief Convert a hexadecimal ASCII number into a D_UINT64 value.
+
+ The function processes all hex digits up to a NUL-terminator, or to the
+ first non-hex character. Only hexadecimal digits are processed, so leading
+ white space, or a leading "0x" prefix are not allowed.
+
+ If pachNum points to an empty string (points to a NUL), this function will
+ return NULL, and the value at *pulNum will not be modified.
+
+ @note This function does not check for overflow. If there are more
+ significant digits than can be represented in a uint64_t variable, the
+ output is unspecified.
+
+ @param pszNum A pointer to a constant array of hex characters.
+ @param pullNum A pointer to the location in which to store the uint64_t
+ result. Upon return, this value will be modified ONLY if
+ the function succeeds and the returned pointer is valid (not
+ NULL).
+
+ @return A pointer to the byte following the converted number, or NULL to
+ indicate failure.
+*/
+const char *RedHtoULL(
+ const char *pszNum,
+ uint64_t *pullNum)
+{
+ uint64_t ullValue = 0U;
+ const char *pszReturn = NULL;
+ uint32_t ulIdx = 0U;
+
+ REDASSERT(pszNum != NULL);
+ REDASSERT(pullNum != NULL);
+
+ while(pszNum[ulIdx] != '\0')
+ {
+ char cDigit = pszNum[ulIdx];
+
+ if(ISDIGIT(cDigit))
+ {
+ cDigit -= '0';
+ }
+ else if(ISHEXDIGITU(cDigit))
+ {
+ cDigit -= ('A' - 10);
+ }
+ else if(ISHEXDIGITL(cDigit))
+ {
+ cDigit -= ('a' - 10);
+ }
+ else
+ {
+ break;
+ }
+
+ REDASSERT((ullValue & UINT64_SUFFIX(0xF000000000000000)) == 0U);
+
+ ullValue <<= 4U;
+ ullValue += cDigit;
+
+ ulIdx++;
+ pszReturn = &pszNum[ulIdx];
+ }
+
+ /* Modify the number returned only if we found one or more valid hex
+ digits.
+ */
+ if(pszReturn != NULL)
+ {
+ *pullNum = ullValue;
+ }
+
+ return pszReturn;
+}
+
+
+/** @brief Convert the ASCII number to a uint32_t value.
+
+ The number may be hex or decimal. Hex numbers must be prefixed by '0x', and
+ they may be upper or lower case. The conversion process will stop with the
+ first non hex or decimal digit.
+
+ If the number is negative (the first character is a '-' sign), the value
+ will be range checked and returned as the equivalent unsigned value.
+
+ @note This function will NOT fail for numbers which exceed the size of a
+ uint32_t value.
+
+ @param pszNum A pointer to the ASCII number to convert
+ @param pulNum A pointer to the uint32_t location to store the result.
+ This value will be modified on return only if the function
+ succeeds and the returned pointer is valid (not NULL).
+
+ @return A pointer to the byte following the converted number, or NULL to
+ indicate failure.
+*/
+const char *RedNtoUL(
+ const char *pszNum,
+ uint32_t *pulNum)
+{
+ bool fNegative = false;
+ uint32_t ulIdx = 0U;
+ const char *pszReturn;
+
+ REDASSERT(pszNum != NULL);
+ REDASSERT(pulNum != NULL);
+
+ if(pszNum[ulIdx] == '-')
+ {
+ fNegative = true;
+ ulIdx++;
+ }
+
+ /* Hex numbers must be prefixed with '0x'.
+ */
+ if((pszNum[ulIdx] == '0') && ((pszNum[ulIdx + 1U] == 'x') || (pszNum[ulIdx + 1U] == 'X')))
+ {
+ ulIdx += 2U;
+
+ if(ISDIGIT(pszNum[ulIdx]) || ISHEXDIGIT(pszNum[ulIdx]))
+ {
+ pszReturn = RedHtoUL(&pszNum[ulIdx], pulNum);
+ }
+ else
+ {
+ pszReturn = NULL;
+ }
+ }
+ else if(ISDIGIT(pszNum[ulIdx]))
+ {
+ uint32_t ulTemp;
+
+ ulTemp = RedAtoI(&pszNum[ulIdx]);
+
+ while(ISDIGIT(pszNum[ulIdx]))
+ {
+ ulIdx++;
+ }
+
+ if(fNegative)
+ {
+ /* Fail if the number is out of range.
+ */
+ if(ulTemp > INT32_MAX)
+ {
+ pszReturn = NULL;
+ }
+ else
+ {
+ *pulNum = -((int32_t)ulTemp);
+ pszReturn = &pszNum[ulIdx];
+ }
+ }
+ else
+ {
+ *pulNum = ulTemp;
+ pszReturn = &pszNum[ulIdx];
+ }
+ }
+ else
+ {
+ /* Return an error if there is not at least one hex or decimal digit.
+ */
+ pszReturn = NULL;
+ }
+
+ return pszReturn;
+}
+
+
+/** @brief Convert the ASCII number pointed to by pachNum to a uint64_t value.
+
+ The number may be hex or decimal. Hex numbers must be prefixed by '0x', and
+ they may be upper or lower case. The conversion process will stop with the
+ first non hex or decimal digit.
+
+ If the number is negative (the first character is a '-' sign), the value
+ will be range checked and returned as the equivalent unsigned value.
+
+ @param pszNum A pointer to the ASCII number to convert.
+ @param pullNum A pointer to the uint64_t location to store the result.
+ This value will be modified on return only if the function
+ succeeds and the returned pointer is valid (not NULL).
+
+ @return A pointer to the byte following the converted number, or NULL to
+ indicate failure.
+*/
+const char *RedNtoULL(
+ const char *pszNum,
+ uint64_t *pullNum)
+{
+ bool fNegative = false;
+ uint32_t ulIdx = 0U;
+ const char *pszReturn;
+
+ REDASSERT(pszNum != NULL);
+ REDASSERT(pullNum != NULL);
+
+ if(pszNum[ulIdx] == '-')
+ {
+ fNegative = true;
+ ulIdx++;
+ }
+
+ /* Hex numbers must be prefixed with '0x'.
+ */
+ if((pszNum[ulIdx] == '0') && ((pszNum[ulIdx + 1U] == 'x') || (pszNum[ulIdx + 1U] == 'X')))
+ {
+ ulIdx += 2U;
+
+ if(ISDIGIT(pszNum[ulIdx]) || ISHEXDIGIT(pszNum[ulIdx]))
+ {
+ pszReturn = RedHtoULL(&pszNum[ulIdx], pullNum);
+ }
+ else
+ {
+ pszReturn = NULL;
+ }
+ }
+ else if(ISDIGIT(pszNum[ulIdx]))
+ {
+ uint64_t ullTemp = 0U;
+
+ while(ISDIGIT(pszNum[ulIdx]))
+ {
+ ullTemp *= 10U;
+ ullTemp += pszNum[ulIdx] - '0';
+ ulIdx++;
+ }
+
+ if(fNegative)
+ {
+ /* Fail if the number is out of range.
+ */
+ if(ullTemp > INT64_MAX)
+ {
+ pszReturn = NULL;
+ }
+ else
+ {
+ *pullNum = (uint64_t)(-((int64_t)ullTemp));
+ pszReturn = &pszNum[ulIdx];
+ }
+ }
+ else
+ {
+ *pullNum = ullTemp;
+ pszReturn = &pszNum[ulIdx];
+ }
+ }
+ else
+ {
+ /* Return an error if there is not at least one hex or decimal digit.
+ */
+ pszReturn = NULL;
+ }
+
+ return pszReturn;
+}
+
+
+/** @brief Convert an ASCII hex or decimal number, which may may have a "B",
+ "KB", or "MB" suffix (case insensitive), to a binary value.
+
+ Hex numbers must be prefixed with "0x".
+
+ @note If there is no postfix, KB is assumed!
+
+ May fail due to bad formatting or overflow.
+
+ @param pszNum A pointer to the ASCII number to convert.
+ @param pulResult A pointer to a uint32_t in which to place the result.
+
+ @return A pointer to the byte following the string, or NULL to indicate an
+ error. In the event of an error, *pulResult will not be modified.
+*/
+const char *RedSizeToUL(
+ const char *pszNum,
+ uint32_t *pulResult)
+{
+ uint32_t ulResult;
+ const char *pszSuffix;
+ const char *pszReturn;
+ uint32_t ulIdx = 0U;
+
+ REDASSERT(pszNum != NULL);
+ REDASSERT(pulResult != NULL);
+
+ /* Do the basic hex/decimal conversion
+ */
+ pszSuffix = RedNtoUL(pszNum, &ulResult);
+ if(pszSuffix != NULL)
+ {
+ if((pszSuffix[ulIdx] == 'B') || (pszSuffix[ulIdx] == 'b'))
+ {
+ ulIdx++;
+ pszReturn = &pszSuffix[ulIdx];
+ }
+ else if( ((pszSuffix[ulIdx] == 'M') || (pszSuffix[ulIdx] == 'm'))
+ && ((pszSuffix[ulIdx + 1U] == 'B') || (pszSuffix[ulIdx + 1U] == 'b')))
+ {
+ ulIdx += 2U;
+
+ if(ulResult > (UINT32_MAX / (1024U * 1024U)))
+ {
+ pszReturn = NULL;
+ }
+ else
+ {
+ ulResult *= 1024U * 1024U;
+ pszReturn = &pszSuffix[ulIdx];
+ }
+ }
+ else
+ {
+ /* The number is either postfixed with "KB" or something
+ else (we don't care), but we must increment the pointer
+ if it is something recognize.
+ */
+ if( ((pszSuffix[ulIdx] == 'K') || (pszSuffix[ulIdx] == 'k'))
+ && ((pszSuffix[ulIdx + 1U] == 'B') || (pszSuffix[ulIdx + 1U] == 'b')))
+ {
+ ulIdx += 2U;
+ }
+
+ /* "B" or "MB" were not specified, so it must be "KB"
+ */
+ if(ulResult > (UINT32_MAX / 1024U))
+ {
+ pszReturn = NULL;
+ }
+ else
+ {
+ ulResult *= 1024UL;
+ pszReturn = &pszSuffix[ulIdx];
+ }
+ }
+
+ if(pszReturn != NULL)
+ {
+ *pulResult = ulResult;
+ }
+ }
+ else
+ {
+ pszReturn = NULL;
+ }
+
+ return pszReturn;
+}
+
-/* ----> DO NOT REMOVE THE FOLLOWING NOTICE <----\r
-\r
- Copyright (c) 2014-2015 Datalight, Inc.\r
- All Rights Reserved Worldwide.\r
-\r
- This program is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; use version 2 of the License.\r
-\r
- This program is distributed in the hope that it will be useful,\r
- but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty\r
- of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License along\r
- with this program; if not, write to the Free Software Foundation, Inc.,\r
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
-*/\r
-/* Businesses and individuals that for commercial or other reasons cannot\r
- comply with the terms of the GPLv2 license may obtain a commercial license\r
- before incorporating Reliance Edge into proprietary software for\r
- distribution in any form. Visit http://www.datalight.com/reliance-edge for\r
- more information.\r
-*/\r
-/** @file\r
- @brief Implements routines for certain 64-bit math operations and simulated\r
- floating point.\r
-\r
- RedUint64DivMod32() and RedUint64DivMod64() are derived from code at\r
- http://www.hackersdelight.org. This web site states explicitly that "You\r
- are free to use, copy, and distribute any of the code on this web site,\r
- whether modified by you or not. You need not give attribution."\r
-*/\r
-#include <redfs.h>\r
-#include <redtestutils.h>\r
-\r
-\r
-static uint32_t nlz64(uint64_t ullValue);\r
-\r
-\r
-/** @brief Return a ratio value formatted as a floating point string accurate to\r
- the specified number of decimal places.\r
-\r
- The function exists to provide floating point style output without using\r
- any actual floating point types.\r
-\r
- This function may scale the numbers down to avoid overflow at the high end.\r
- Likewise, potential divide-by-zero errors are internally avoided. Here are\r
- some examples:\r
-\r
- Dividend | Divisor | DecPlaces | Result\r
- -------- | ------- | --------- | ------\r
- 12133 | 28545 | 2 | "0.42"\r
- 1539 | 506 | 2 | "3.04"\r
-\r
- To get a number formatted as a percentage, take the take the portion of the\r
- total (normally the smaller part), multiply it by 100, and pass it to this\r
- function as the Dividend, pass the "total" value to this function as the\r
- Divisor, and specify the desired number of decimal places.\r
-\r
- For example, if you have a disk format overhead value of N blocks out of a\r
- total of Y blocks on the disk, and you want to display the format overhead\r
- as a percentage, you would use a function call\r
- similar to:\r
-\r
- ~~~{.c}\r
- RedRatio(szBuffer, sizeof(szBuffer), N*100U, Y, 2U);\r
- ~~~\r
-\r
- If N=145, Y=4096, and decimal places is 2, the resulting output would be\r
- "3.54".\r
-\r
- The string returned will always be null-terminated, even if it means\r
- stomping on the least significant decimal digit.\r
-\r
- If either the dividend or divisor values are zero, the string "0.0" will be\r
- returned, with the prescribed number of decimal places.\r
-\r
- @note This function has "reasonable" limits which meet the needs of the\r
- various supplemental utilities which use this function. Extremely\r
- large ratios, or using many decimal places may not function as\r
- desired.\r
-\r
- Parameters:\r
- @param pBuffer A pointer to the buffer in which to store the null\r
- terminated results.\r
- @param ulBufferLen The length of the output buffer.\r
- @param ullDividend The "total" value to divide.\r
- @param ullDivisor The portion of ullDividend for which to calculate the\r
- ratio (may be greater than ulDividend).\r
- @param ulDecPlaces The number of decimal places to use, from 0 to 9.\r
-\r
- @return @p pBuffer.\r
-*/\r
-char *RedRatio(\r
- char *pBuffer,\r
- uint32_t ulBufferLen,\r
- uint64_t ullDividend,\r
- uint64_t ullDivisor,\r
- uint32_t ulDecPlaces)\r
-{\r
- REDASSERT(pBuffer != NULL);\r
- REDASSERT(ulBufferLen > 0U);\r
- REDASSERT(ulDecPlaces <= 9U); /* arbitrary */\r
-\r
- if((ullDivisor > 0U) && (ullDividend > 0U))\r
- {\r
- uint32_t ii;\r
- uint32_t ulFactor = 1U;\r
- uint64_t ullDecimal;\r
- uint64_t ullTemp;\r
-\r
- for(ii = 1U; ii <= ulDecPlaces; ii++)\r
- {\r
- ulFactor *= 10U;\r
- }\r
-\r
- ullDecimal = RedMulDiv64(ullDividend, ulFactor, ullDivisor);\r
-\r
- /* Shouldn't really be calling this function in a situation where we\r
- can overflow at this point...\r
- */\r
- REDASSERT(ullDecimal != UINT64_MAX);\r
-\r
- if(ullDivisor <= ullDividend)\r
- {\r
- uint32_t ulDecimal;\r
-\r
- (void)RedUint64DivMod32(ullDecimal, ulFactor, &ulDecimal);\r
- ullDecimal = ulDecimal;\r
- }\r
-\r
- ullTemp = RedUint64DivMod64(ullDividend, ullDivisor, NULL);\r
-\r
- if(ulDecPlaces > 0U)\r
- {\r
- RedSNPrintf(pBuffer, ulBufferLen, "%llu.%0*llu", (unsigned long long)ullTemp,\r
- (unsigned)ulDecPlaces, (unsigned long long)ullDecimal);\r
- }\r
- else\r
- {\r
- RedSNPrintf(pBuffer, ulBufferLen, "%llu", (unsigned long long)ullTemp);\r
- }\r
- }\r
- else\r
- {\r
- /* If either the dividend or divisor is zero, then just output a "0.0"\r
- string with the prescribed number of decimal places.\r
- */\r
- if(ulDecPlaces > 0U)\r
- {\r
- RedSNPrintf(pBuffer, ulBufferLen, "0.%0*u", (unsigned)ulDecPlaces, 0U);\r
- }\r
- else\r
- {\r
- RedStrNCpy(pBuffer, "0", ulBufferLen);\r
- }\r
- }\r
-\r
- /* Ensure the returned buffer is always null-terminated\r
- */\r
- pBuffer[ulBufferLen - 1U] = '\0';\r
-\r
- return pBuffer;\r
-}\r
-\r
-\r
-/** @brief Multiply 64-bit and 32-bit numbers, and divide by a 64-bit number,\r
- returning a 64-bit result.\r
-\r
- @note This function may return an approximate value if multiplying\r
- @p ullBase and @p ulMultplier results in a number larger than 64-bits\r
- _and_ this cannot be avoided by scaling.\r
-\r
- @param ullBase The base 64-bit number number.\r
- @param ulMultiplier The 32-bit number by which to multiply.\r
- @param ullDivisor The 64-bit number by which to divide.\r
-\r
- @return The 64-bit unsigned integer result. Always returns zero if either\r
- @p ullBase or @p ulMultiplier are zero (regardless what\r
- @p ullDivisor is). Returns UINT64_MAX if an overflow condition\r
- occurred, or if @p ullDivisor is zero.\r
-*/\r
-uint64_t RedMulDiv64(\r
- uint64_t ullBase,\r
- uint32_t ulMultiplier,\r
- uint64_t ullDivisor)\r
-{\r
- uint64_t ullTemp;\r
-\r
- /* Result would always be zero if either of these are zero. Specifically\r
- test this case before looking for a zero divisor.\r
- */\r
- if((ullBase == 0U) || (ulMultiplier == 0U))\r
- {\r
- return 0U;\r
- }\r
-\r
- if(ullDivisor == 0U)\r
- {\r
- return UINT64_MAX;\r
- }\r
-\r
- /* Since we don't have the ability (yet) to use 128-bit numbers, we jump\r
- through the following hoops (in order) to try to determine the proper\r
- results without losing precision:\r
-\r
- 1) Shift the divisor and one of the multiplicands as many times as is\r
- necessary to reduce the scale -- only if it can be done without\r
- losing precision.\r
- 2) Divide one of the multiplicands by the divisor first, but only if it\r
- divides evenly, preserving precision.\r
- 3) Same as #2, but try it for the other multiplicand.\r
- 4) Last ditch, divide the larger multiplicand by the divisor first, then\r
- do the multiply. This <WILL> lose precision.\r
-\r
- These solutions are identified as CODE-PATHs #1-4 which are used to\r
- identify the matching tests in dltmain.c.\r
-\r
- Note that execution might partially include CODE-PATH #1 up until\r
- shifting can no longer be done without losing precision. In that case,\r
- one of the three remaining options will be used.\r
- */\r
-\r
- ullTemp = RedUint64DivMod32(UINT64_MAX, ulMultiplier, NULL);\r
- while(ullBase > ullTemp)\r
- {\r
- uint64_t ullMod;\r
- uint64_t ullBaseTemp;\r
- uint64_t ullWideMultiplier;\r
-\r
- /* CODE-PATH #1\r
- */\r
- /* So long as ulDivisor, and at least one of the other numbers, are\r
- evenly divisible by 2, we can scale the numbers so the result does\r
- not overflow the intermediate 64-bit value.\r
- */\r
- if((ullDivisor & 1U) == 0U)\r
- {\r
- if((ullBase & 1U) == 0U)\r
- {\r
- /* CODE-PATH #1a\r
- */\r
- ullDivisor >>= 1U;\r
- ullBase >>= 1U;\r
- continue;\r
- }\r
-\r
- if(((ulMultiplier & 1U) == 0U) && ((ullTemp & UINT64_SUFFIX(0x8000000000000000)) == 0U))\r
- {\r
- /* CODE-PATH #1b\r
- */\r
- ullDivisor >>= 1U;\r
- ulMultiplier >>= 1U;\r
- ullTemp <<= 1U;\r
- continue;\r
- }\r
- }\r
-\r
- /* If we get to this point, the above method (#1) cannot be used\r
- because not enough of the numbers are even long enough to scale the\r
- operands down. We'll see if either multiplicand is evenly divisble\r
- by ulDivisor, and if so, do the divide first, then the multiply.\r
- (Note that once we get to this point, we will never exercise the\r
- while{} loop anymore.)\r
- */\r
-\r
- /* CODE-PATH #2\r
- */\r
- ullBaseTemp = RedUint64DivMod64(ullBase, ullDivisor, &ullMod);\r
- if(ullMod == 0U)\r
- {\r
- /* Evenly divides, so check that we won't overflow, and finish up.\r
- */\r
- ullBase = ullBaseTemp;\r
- if(ullBase > ullTemp)\r
- {\r
- return UINT64_MAX;\r
- }\r
- else\r
- {\r
- /* We've validated that this will not overflow.\r
- */\r
- ullBase *= ulMultiplier;\r
- return ullBase;\r
- }\r
- }\r
-\r
- /* CODE-PATH #3\r
- */\r
- ullWideMultiplier = RedUint64DivMod64(ulMultiplier, ullDivisor, &ullMod);\r
- if(ullMod == 0U)\r
- {\r
- /* Evenly divides, so check that we won't overflow, and finish up.\r
- */\r
-\r
- /* Must recalculate ullTemp relative to ullBase\r
- */\r
- ullTemp = RedUint64DivMod64(UINT64_MAX, ullBase, NULL);\r
- if(ullWideMultiplier > ullTemp)\r
- {\r
- return UINT64_MAX;\r
- }\r
- else\r
- {\r
- uint32_t ulNarrowMultiplier = (uint32_t)ullWideMultiplier;\r
-\r
- /* We've validated that this will not overflow.\r
- */\r
- ullBase *= ulNarrowMultiplier;\r
- return ullBase;\r
- }\r
- }\r
-\r
- /* CODE-PATH #4\r
-\r
- Neither of the multipliers is evenly divisible by the divisor, so\r
- just punt and divide the larger number first, then do the final\r
- multiply.\r
-\r
- All the other attempts above would preserve precision -- this is the\r
- only case where precision may be lost.\r
- */\r
-\r
- /* If necessary reverse the ullBase and ulMultiplier operands so that\r
- ullBase contains the larger of the two values.\r
- */\r
- if(ullBase < ulMultiplier)\r
- {\r
- uint32_t ulTemp = ulMultiplier;\r
-\r
- ulMultiplier = (uint32_t)ullBase;\r
- ullBase = ulTemp;\r
- }\r
-\r
- ullBase = RedUint64DivMod64(ullBase, ullDivisor, NULL);\r
- ullTemp = RedUint64DivMod32(UINT64_MAX, ulMultiplier, NULL);\r
- if(ullBase > ullTemp)\r
- {\r
- return UINT64_MAX;\r
- }\r
- else\r
- {\r
- ullBase *= ulMultiplier;\r
- return ullBase;\r
- }\r
- }\r
-\r
- /* We only get to this point if either there was never any chance of\r
- overflow, or if the pure shifting mechanism succeeded in reducing\r
- the scale so overflow is not a problem.\r
- */\r
-\r
- ullBase *= ulMultiplier;\r
- ullBase = RedUint64DivMod64(ullBase, ullDivisor, NULL);\r
-\r
- return ullBase;\r
-}\r
-\r
-\r
-/** @brief Divide a 64-bit value by a 32-bit value, returning the quotient and\r
- the remainder.\r
-\r
- Essentially this function does the following:\r
-\r
- ~~~{.c}\r
- if(pulRemainder != NULL)\r
- {\r
- *pulRemainder = (uint32_t)(ullDividend % ulDivisor);\r
- }\r
- return ullDividend / ulDivisor;\r
- ~~~\r
-\r
- However, it does so without ever actually dividing/modulating a 64-bit\r
- value, since such operations are not allowed in all environments.\r
-\r
- @param ullDividend The value to divide.\r
- @param ulDivisor The value to divide by.\r
- @param pulRemander Populated with the remainder; may be NULL.\r
-\r
- @return The quotient (result of the division).\r
-*/\r
-uint64_t RedUint64DivMod32(\r
- uint64_t ullDividend,\r
- uint32_t ulDivisor,\r
- uint32_t *pulRemainder)\r
-{\r
- uint64_t ullQuotient;\r
- uint32_t ulResultRemainder;\r
-\r
- /* Check for divide by zero.\r
- */\r
- if(ulDivisor == 0U)\r
- {\r
- REDERROR();\r
-\r
- /* Nonsense value if no asserts.\r
- */\r
- ullQuotient = UINT64_SUFFIX(0xFFFFFFFFFFFFFBAD);\r
- ulResultRemainder = 0xFFFFFBADU;\r
- }\r
- else if(ullDividend <= UINT32_MAX)\r
- {\r
- uint32_t ulDividend = (uint32_t)ullDividend;\r
-\r
- ullQuotient = ulDividend / ulDivisor;\r
- ulResultRemainder = ulDividend % ulDivisor;\r
- }\r
- else\r
- {\r
- uint32_t ulResultHi;\r
- uint32_t ulResultLo;\r
- uint32_t ulRemainder;\r
- uint8_t bIndex;\r
- uint32_t ulThisDivision;\r
- uint32_t ulMask;\r
- uint8_t ucNextValue;\r
- uint32_t ulInterimHi, ulInterimLo;\r
- uint32_t ulLowDword = (uint32_t)ullDividend;\r
- uint32_t ulHighDword = (uint32_t)(ullDividend >> 32U);\r
-\r
- /* Compute the high part and get the remainder\r
- */\r
- ulResultHi = ulHighDword / ulDivisor;\r
- ulResultLo = 0U;\r
- ulRemainder = ulHighDword % ulDivisor;\r
-\r
- /* Compute the low part\r
- */\r
- ulMask = 0xFF000000U;\r
- for(bIndex = 0U; bIndex < sizeof(uint32_t); bIndex++)\r
- {\r
- ucNextValue = (uint8_t)((ulLowDword & ulMask) >> ((sizeof(uint32_t) - 1U - bIndex) * 8U));\r
- ulInterimHi = ulRemainder >> 24U;\r
- ulInterimLo = (ulRemainder << 8U) | ucNextValue;\r
- ulThisDivision = 0U;\r
- while(ulInterimHi != 0U)\r
- {\r
- uint64_t ullInterim = ((uint64_t)ulInterimHi << 32U) + ulInterimLo;\r
-\r
- ullInterim -= ulDivisor;\r
- ulThisDivision++;\r
-\r
- ulInterimHi = (uint32_t)(ullInterim >> 32U);\r
- ulInterimLo = (uint32_t)ullInterim;\r
- }\r
- ulThisDivision += ulInterimLo / ulDivisor;\r
- ulRemainder = ulInterimLo % ulDivisor;\r
- ulResultLo <<= 8U;\r
- ulResultLo += ulThisDivision;\r
- ulMask >>= 8U;\r
- }\r
-\r
- ullQuotient = ((uint64_t)ulResultHi << 32U) + ulResultLo;\r
- ulResultRemainder = (uint32_t)(ullDividend - (ullQuotient * ulDivisor));\r
- }\r
-\r
- if(pulRemainder != NULL)\r
- {\r
- *pulRemainder = ulResultRemainder;\r
- }\r
-\r
- return ullQuotient;\r
-}\r
-\r
-\r
-/** @brief Divide a 64-bit value by a 64-bit value, returning the quotient and\r
- the remainder.\r
-\r
- Essentially this function does the following:\r
-\r
- ~~~{.c}\r
- if(pullRemainder != NULL)\r
- {\r
- *pullRemainder = ullDividend % ullDivisor;\r
- }\r
- return ullDividend / ullDivisor;\r
- ~~~\r
-\r
- However, it does so without ever actually dividing/modulating a 64-bit\r
- value, since such operations are not allowed in all environments.\r
-\r
- @param ullDividend The value to divide.\r
- @param ullDivisor The value to divide by.\r
- @param pullRemander Populated with the remainder; may be NULL.\r
-\r
- @return The quotient (result of the division).\r
-*/\r
-uint64_t RedUint64DivMod64(\r
- uint64_t ullDividend,\r
- uint64_t ullDivisor,\r
- uint64_t *pullRemainder)\r
-{\r
- /* The variables u0, u1, etc. take on only 32-bit values, but they are\r
- declared uint64_t to avoid some compiler warning messages and to avoid\r
- some unnecessary EXTRs that the compiler would put in, to convert\r
- uint64_ts to ints.\r
- */\r
- uint64_t u0;\r
- uint64_t u1;\r
- uint64_t q0;\r
- uint64_t q1;\r
- uint64_t ullQuotient;\r
-\r
- /* First the procedure takes care of the case in which the divisor is a\r
- 32-bit quantity. There are two subcases: (1) If the left half of the\r
- dividend is less than the divisor, one execution of RedUint64DivMod32()\r
- is all that is required (overflow is not possible). (2) Otherwise it\r
- does two divisions, using the grade school method.\r
- */\r
-\r
- if((ullDivisor >> 32U) == 0U)\r
- {\r
- if((ullDividend >> 32U) < ullDivisor)\r
- {\r
- /* If ullDividend/ullDivisor cannot overflow, just do one division.\r
- */\r
- ullQuotient = RedUint64DivMod32(ullDividend, (uint32_t)ullDivisor, NULL);\r
- }\r
- else\r
- {\r
- uint32_t k;\r
-\r
- /* If ullDividend/ullDivisor would overflow:\r
- */\r
-\r
- /* Break ullDividend up into two halves.\r
- */\r
- u1 = ullDividend >> 32U;\r
- u0 = ullDividend & 0xFFFFFFFFU;\r
-\r
- /* First quotient digit and first remainder.\r
- */\r
- q1 = RedUint64DivMod32(u1, (uint32_t)ullDivisor, &k);\r
-\r
- /* 2nd quot. digit.\r
- */\r
- q0 = RedUint64DivMod32(((uint64_t)k << 32U) + u0, (uint32_t)ullDivisor, NULL);\r
-\r
- ullQuotient = (q1 << 32U) + q0;\r
- }\r
- }\r
- else\r
- {\r
- uint64_t n;\r
- uint64_t v1;\r
-\r
- n = nlz64(ullDivisor); /* 0 <= n <= 31. */\r
- v1 = (ullDivisor << n) >> 32U; /* Normalize the divisor so its MSB is 1. */\r
- u1 = ullDividend >> 1U; /* To ensure no overflow. */\r
-\r
- /* Get quotient from divide unsigned insn.\r
- */\r
- q1 = RedUint64DivMod32(u1, (uint32_t)v1, NULL);\r
-\r
- q0 = (q1 << n) >> 31U; /* Undo normalization and division of ullDividend by 2. */\r
-\r
- /* Make q0 correct or too small by 1.\r
- */\r
- if(q0 != 0U)\r
- {\r
- q0--;\r
- }\r
-\r
- if((ullDividend - (q0 * ullDivisor)) >= ullDivisor)\r
- {\r
- q0++; /* Now q0 is correct. */\r
- }\r
-\r
- ullQuotient = q0;\r
- }\r
-\r
- if(pullRemainder != NULL)\r
- {\r
- *pullRemainder = ullDividend - (ullQuotient * ullDivisor);\r
- }\r
-\r
- return ullQuotient;\r
-}\r
-\r
-\r
-/** @brief Compute the number of leading zeroes in a 64-bit value.\r
-\r
- @param ullValue The value for which to compute the NLZ.\r
-\r
- @return The number of leading zeroes in @p ullValue.\r
-*/\r
-static uint32_t nlz64(\r
- uint64_t ullValue)\r
-{\r
- uint32_t n;\r
-\r
- if(ullValue == 0U)\r
- {\r
- n = 64U;\r
- }\r
- else\r
- {\r
- uint64_t x = ullValue;\r
-\r
- n = 0U;\r
-\r
- if(x <= UINT64_SUFFIX(0x00000000FFFFFFFF))\r
- {\r
- n += 32U;\r
- x <<= 32U;\r
- }\r
-\r
- if(x <= UINT64_SUFFIX(0x0000FFFFFFFFFFFF))\r
- {\r
- n += 16U;\r
- x <<= 16U;\r
- }\r
-\r
- if(x <= UINT64_SUFFIX(0x00FFFFFFFFFFFFFF))\r
- {\r
- n += 8U;\r
- x <<= 8U;\r
- }\r
-\r
- if(x <= UINT64_SUFFIX(0x0FFFFFFFFFFFFFFF))\r
- {\r
- n += 4U;\r
- x <<= 4U;\r
- }\r
-\r
- if(x <= UINT64_SUFFIX(0x3FFFFFFFFFFFFFFF))\r
- {\r
- n += 2U;\r
- x <<= 2U;\r
- }\r
-\r
- if(x <= UINT64_SUFFIX(0x7FFFFFFFFFFFFFFF))\r
- {\r
- n += 1;\r
- }\r
- }\r
-\r
- return n;\r
-}\r
-\r
+/* ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
+
+ Copyright (c) 2014-2015 Datalight, Inc.
+ All Rights Reserved Worldwide.
+
+ 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; use version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but "AS-IS," 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+/* Businesses and individuals that for commercial or other reasons cannot
+ comply with the terms of the GPLv2 license may obtain a commercial license
+ before incorporating Reliance Edge into proprietary software for
+ distribution in any form. Visit http://www.datalight.com/reliance-edge for
+ more information.
+*/
+/** @file
+ @brief Implements routines for certain 64-bit math operations and simulated
+ floating point.
+
+ RedUint64DivMod32() and RedUint64DivMod64() are derived from code at
+ http://www.hackersdelight.org. This web site states explicitly that "You
+ are free to use, copy, and distribute any of the code on this web site,
+ whether modified by you or not. You need not give attribution."
+*/
+#include <redfs.h>
+#include <redtestutils.h>
+
+
+static uint32_t nlz64(uint64_t ullValue);
+
+
+/** @brief Return a ratio value formatted as a floating point string accurate to
+ the specified number of decimal places.
+
+ The function exists to provide floating point style output without using
+ any actual floating point types.
+
+ This function may scale the numbers down to avoid overflow at the high end.
+ Likewise, potential divide-by-zero errors are internally avoided. Here are
+ some examples:
+
+ Dividend | Divisor | DecPlaces | Result
+ -------- | ------- | --------- | ------
+ 12133 | 28545 | 2 | "0.42"
+ 1539 | 506 | 2 | "3.04"
+
+ To get a number formatted as a percentage, take the take the portion of the
+ total (normally the smaller part), multiply it by 100, and pass it to this
+ function as the Dividend, pass the "total" value to this function as the
+ Divisor, and specify the desired number of decimal places.
+
+ For example, if you have a disk format overhead value of N blocks out of a
+ total of Y blocks on the disk, and you want to display the format overhead
+ as a percentage, you would use a function call
+ similar to:
+
+ ~~~{.c}
+ RedRatio(szBuffer, sizeof(szBuffer), N*100U, Y, 2U);
+ ~~~
+
+ If N=145, Y=4096, and decimal places is 2, the resulting output would be
+ "3.54".
+
+ The string returned will always be null-terminated, even if it means
+ stomping on the least significant decimal digit.
+
+ If either the dividend or divisor values are zero, the string "0.0" will be
+ returned, with the prescribed number of decimal places.
+
+ @note This function has "reasonable" limits which meet the needs of the
+ various supplemental utilities which use this function. Extremely
+ large ratios, or using many decimal places may not function as
+ desired.
+
+ Parameters:
+ @param pBuffer A pointer to the buffer in which to store the null
+ terminated results.
+ @param ulBufferLen The length of the output buffer.
+ @param ullDividend The "total" value to divide.
+ @param ullDivisor The portion of ullDividend for which to calculate the
+ ratio (may be greater than ulDividend).
+ @param ulDecPlaces The number of decimal places to use, from 0 to 9.
+
+ @return @p pBuffer.
+*/
+char *RedRatio(
+ char *pBuffer,
+ uint32_t ulBufferLen,
+ uint64_t ullDividend,
+ uint64_t ullDivisor,
+ uint32_t ulDecPlaces)
+{
+ REDASSERT(pBuffer != NULL);
+ REDASSERT(ulBufferLen > 0U);
+ REDASSERT(ulDecPlaces <= 9U); /* arbitrary */
+
+ if((ullDivisor > 0U) && (ullDividend > 0U))
+ {
+ uint32_t ii;
+ uint32_t ulFactor = 1U;
+ uint64_t ullDecimal;
+ uint64_t ullTemp;
+
+ for(ii = 1U; ii <= ulDecPlaces; ii++)
+ {
+ ulFactor *= 10U;
+ }
+
+ ullDecimal = RedMulDiv64(ullDividend, ulFactor, ullDivisor);
+
+ /* Shouldn't really be calling this function in a situation where we
+ can overflow at this point...
+ */
+ REDASSERT(ullDecimal != UINT64_MAX);
+
+ if(ullDivisor <= ullDividend)
+ {
+ uint32_t ulDecimal;
+
+ (void)RedUint64DivMod32(ullDecimal, ulFactor, &ulDecimal);
+ ullDecimal = ulDecimal;
+ }
+
+ ullTemp = RedUint64DivMod64(ullDividend, ullDivisor, NULL);
+
+ if(ulDecPlaces > 0U)
+ {
+ RedSNPrintf(pBuffer, ulBufferLen, "%llu.%0*llu", (unsigned long long)ullTemp,
+ (unsigned)ulDecPlaces, (unsigned long long)ullDecimal);
+ }
+ else
+ {
+ RedSNPrintf(pBuffer, ulBufferLen, "%llu", (unsigned long long)ullTemp);
+ }
+ }
+ else
+ {
+ /* If either the dividend or divisor is zero, then just output a "0.0"
+ string with the prescribed number of decimal places.
+ */
+ if(ulDecPlaces > 0U)
+ {
+ RedSNPrintf(pBuffer, ulBufferLen, "0.%0*u", (unsigned)ulDecPlaces, 0U);
+ }
+ else
+ {
+ RedStrNCpy(pBuffer, "0", ulBufferLen);
+ }
+ }
+
+ /* Ensure the returned buffer is always null-terminated
+ */
+ pBuffer[ulBufferLen - 1U] = '\0';
+
+ return pBuffer;
+}
+
+
+/** @brief Multiply 64-bit and 32-bit numbers, and divide by a 64-bit number,
+ returning a 64-bit result.
+
+ @note This function may return an approximate value if multiplying
+ @p ullBase and @p ulMultplier results in a number larger than 64-bits
+ _and_ this cannot be avoided by scaling.
+
+ @param ullBase The base 64-bit number number.
+ @param ulMultiplier The 32-bit number by which to multiply.
+ @param ullDivisor The 64-bit number by which to divide.
+
+ @return The 64-bit unsigned integer result. Always returns zero if either
+ @p ullBase or @p ulMultiplier are zero (regardless what
+ @p ullDivisor is). Returns UINT64_MAX if an overflow condition
+ occurred, or if @p ullDivisor is zero.
+*/
+uint64_t RedMulDiv64(
+ uint64_t ullBase,
+ uint32_t ulMultiplier,
+ uint64_t ullDivisor)
+{
+ uint64_t ullTemp;
+
+ /* Result would always be zero if either of these are zero. Specifically
+ test this case before looking for a zero divisor.
+ */
+ if((ullBase == 0U) || (ulMultiplier == 0U))
+ {
+ return 0U;
+ }
+
+ if(ullDivisor == 0U)
+ {
+ return UINT64_MAX;
+ }
+
+ /* Since we don't have the ability (yet) to use 128-bit numbers, we jump
+ through the following hoops (in order) to try to determine the proper
+ results without losing precision:
+
+ 1) Shift the divisor and one of the multiplicands as many times as is
+ necessary to reduce the scale -- only if it can be done without
+ losing precision.
+ 2) Divide one of the multiplicands by the divisor first, but only if it
+ divides evenly, preserving precision.
+ 3) Same as #2, but try it for the other multiplicand.
+ 4) Last ditch, divide the larger multiplicand by the divisor first, then
+ do the multiply. This <WILL> lose precision.
+
+ These solutions are identified as CODE-PATHs #1-4 which are used to
+ identify the matching tests in dltmain.c.
+
+ Note that execution might partially include CODE-PATH #1 up until
+ shifting can no longer be done without losing precision. In that case,
+ one of the three remaining options will be used.
+ */
+
+ ullTemp = RedUint64DivMod32(UINT64_MAX, ulMultiplier, NULL);
+ while(ullBase > ullTemp)
+ {
+ uint64_t ullMod;
+ uint64_t ullBaseTemp;
+ uint64_t ullWideMultiplier;
+
+ /* CODE-PATH #1
+ */
+ /* So long as ulDivisor, and at least one of the other numbers, are
+ evenly divisible by 2, we can scale the numbers so the result does
+ not overflow the intermediate 64-bit value.
+ */
+ if((ullDivisor & 1U) == 0U)
+ {
+ if((ullBase & 1U) == 0U)
+ {
+ /* CODE-PATH #1a
+ */
+ ullDivisor >>= 1U;
+ ullBase >>= 1U;
+ continue;
+ }
+
+ if(((ulMultiplier & 1U) == 0U) && ((ullTemp & UINT64_SUFFIX(0x8000000000000000)) == 0U))
+ {
+ /* CODE-PATH #1b
+ */
+ ullDivisor >>= 1U;
+ ulMultiplier >>= 1U;
+ ullTemp <<= 1U;
+ continue;
+ }
+ }
+
+ /* If we get to this point, the above method (#1) cannot be used
+ because not enough of the numbers are even long enough to scale the
+ operands down. We'll see if either multiplicand is evenly divisble
+ by ulDivisor, and if so, do the divide first, then the multiply.
+ (Note that once we get to this point, we will never exercise the
+ while{} loop anymore.)
+ */
+
+ /* CODE-PATH #2
+ */
+ ullBaseTemp = RedUint64DivMod64(ullBase, ullDivisor, &ullMod);
+ if(ullMod == 0U)
+ {
+ /* Evenly divides, so check that we won't overflow, and finish up.
+ */
+ ullBase = ullBaseTemp;
+ if(ullBase > ullTemp)
+ {
+ return UINT64_MAX;
+ }
+ else
+ {
+ /* We've validated that this will not overflow.
+ */
+ ullBase *= ulMultiplier;
+ return ullBase;
+ }
+ }
+
+ /* CODE-PATH #3
+ */
+ ullWideMultiplier = RedUint64DivMod64(ulMultiplier, ullDivisor, &ullMod);
+ if(ullMod == 0U)
+ {
+ /* Evenly divides, so check that we won't overflow, and finish up.
+ */
+
+ /* Must recalculate ullTemp relative to ullBase
+ */
+ ullTemp = RedUint64DivMod64(UINT64_MAX, ullBase, NULL);
+ if(ullWideMultiplier > ullTemp)
+ {
+ return UINT64_MAX;
+ }
+ else
+ {
+ uint32_t ulNarrowMultiplier = (uint32_t)ullWideMultiplier;
+
+ /* We've validated that this will not overflow.
+ */
+ ullBase *= ulNarrowMultiplier;
+ return ullBase;
+ }
+ }
+
+ /* CODE-PATH #4
+
+ Neither of the multipliers is evenly divisible by the divisor, so
+ just punt and divide the larger number first, then do the final
+ multiply.
+
+ All the other attempts above would preserve precision -- this is the
+ only case where precision may be lost.
+ */
+
+ /* If necessary reverse the ullBase and ulMultiplier operands so that
+ ullBase contains the larger of the two values.
+ */
+ if(ullBase < ulMultiplier)
+ {
+ uint32_t ulTemp = ulMultiplier;
+
+ ulMultiplier = (uint32_t)ullBase;
+ ullBase = ulTemp;
+ }
+
+ ullBase = RedUint64DivMod64(ullBase, ullDivisor, NULL);
+ ullTemp = RedUint64DivMod32(UINT64_MAX, ulMultiplier, NULL);
+ if(ullBase > ullTemp)
+ {
+ return UINT64_MAX;
+ }
+ else
+ {
+ ullBase *= ulMultiplier;
+ return ullBase;
+ }
+ }
+
+ /* We only get to this point if either there was never any chance of
+ overflow, or if the pure shifting mechanism succeeded in reducing
+ the scale so overflow is not a problem.
+ */
+
+ ullBase *= ulMultiplier;
+ ullBase = RedUint64DivMod64(ullBase, ullDivisor, NULL);
+
+ return ullBase;
+}
+
+
+/** @brief Divide a 64-bit value by a 32-bit value, returning the quotient and
+ the remainder.
+
+ Essentially this function does the following:
+
+ ~~~{.c}
+ if(pulRemainder != NULL)
+ {
+ *pulRemainder = (uint32_t)(ullDividend % ulDivisor);
+ }
+ return ullDividend / ulDivisor;
+ ~~~
+
+ However, it does so without ever actually dividing/modulating a 64-bit
+ value, since such operations are not allowed in all environments.
+
+ @param ullDividend The value to divide.
+ @param ulDivisor The value to divide by.
+ @param pulRemainder Populated with the remainder; may be NULL.
+
+ @return The quotient (result of the division).
+*/
+uint64_t RedUint64DivMod32(
+ uint64_t ullDividend,
+ uint32_t ulDivisor,
+ uint32_t *pulRemainder)
+{
+ uint64_t ullQuotient;
+ uint32_t ulResultRemainder;
+
+ /* Check for divide by zero.
+ */
+ if(ulDivisor == 0U)
+ {
+ REDERROR();
+
+ /* Nonsense value if no asserts.
+ */
+ ullQuotient = UINT64_SUFFIX(0xFFFFFFFFFFFFFBAD);
+ ulResultRemainder = 0xFFFFFBADU;
+ }
+ else if(ullDividend <= UINT32_MAX)
+ {
+ uint32_t ulDividend = (uint32_t)ullDividend;
+
+ ullQuotient = ulDividend / ulDivisor;
+ ulResultRemainder = ulDividend % ulDivisor;
+ }
+ else
+ {
+ uint32_t ulResultHi;
+ uint32_t ulResultLo;
+ uint32_t ulRemainder;
+ uint8_t bIndex;
+ uint32_t ulThisDivision;
+ uint32_t ulMask;
+ uint8_t ucNextValue;
+ uint32_t ulInterimHi, ulInterimLo;
+ uint32_t ulLowDword = (uint32_t)ullDividend;
+ uint32_t ulHighDword = (uint32_t)(ullDividend >> 32U);
+
+ /* Compute the high part and get the remainder
+ */
+ ulResultHi = ulHighDword / ulDivisor;
+ ulResultLo = 0U;
+ ulRemainder = ulHighDword % ulDivisor;
+
+ /* Compute the low part
+ */
+ ulMask = 0xFF000000U;
+ for(bIndex = 0U; bIndex < sizeof(uint32_t); bIndex++)
+ {
+ ucNextValue = (uint8_t)((ulLowDword & ulMask) >> ((sizeof(uint32_t) - 1U - bIndex) * 8U));
+ ulInterimHi = ulRemainder >> 24U;
+ ulInterimLo = (ulRemainder << 8U) | ucNextValue;
+ ulThisDivision = 0U;
+ while(ulInterimHi != 0U)
+ {
+ uint64_t ullInterim = ((uint64_t)ulInterimHi << 32U) + ulInterimLo;
+
+ ullInterim -= ulDivisor;
+ ulThisDivision++;
+
+ ulInterimHi = (uint32_t)(ullInterim >> 32U);
+ ulInterimLo = (uint32_t)ullInterim;
+ }
+ ulThisDivision += ulInterimLo / ulDivisor;
+ ulRemainder = ulInterimLo % ulDivisor;
+ ulResultLo <<= 8U;
+ ulResultLo += ulThisDivision;
+ ulMask >>= 8U;
+ }
+
+ ullQuotient = ((uint64_t)ulResultHi << 32U) + ulResultLo;
+ ulResultRemainder = (uint32_t)(ullDividend - (ullQuotient * ulDivisor));
+ }
+
+ if(pulRemainder != NULL)
+ {
+ *pulRemainder = ulResultRemainder;
+ }
+
+ return ullQuotient;
+}
+
+
+/** @brief Divide a 64-bit value by a 64-bit value, returning the quotient and
+ the remainder.
+
+ Essentially this function does the following:
+
+ ~~~{.c}
+ if(pullRemainder != NULL)
+ {
+ *pullRemainder = ullDividend % ullDivisor;
+ }
+ return ullDividend / ullDivisor;
+ ~~~
+
+ However, it does so without ever actually dividing/modulating a 64-bit
+ value, since such operations are not allowed in all environments.
+
+ @param ullDividend The value to divide.
+ @param ullDivisor The value to divide by.
+ @param pullRemainder Populated with the remainder; may be NULL.
+
+ @return The quotient (result of the division).
+*/
+uint64_t RedUint64DivMod64(
+ uint64_t ullDividend,
+ uint64_t ullDivisor,
+ uint64_t *pullRemainder)
+{
+ /* The variables u0, u1, etc. take on only 32-bit values, but they are
+ declared uint64_t to avoid some compiler warning messages and to avoid
+ some unnecessary EXTRs that the compiler would put in, to convert
+ uint64_ts to ints.
+ */
+ uint64_t u0;
+ uint64_t u1;
+ uint64_t q0;
+ uint64_t q1;
+ uint64_t ullQuotient;
+
+ /* First the procedure takes care of the case in which the divisor is a
+ 32-bit quantity. There are two subcases: (1) If the left half of the
+ dividend is less than the divisor, one execution of RedUint64DivMod32()
+ is all that is required (overflow is not possible). (2) Otherwise it
+ does two divisions, using the grade school method.
+ */
+
+ if((ullDivisor >> 32U) == 0U)
+ {
+ if((ullDividend >> 32U) < ullDivisor)
+ {
+ /* If ullDividend/ullDivisor cannot overflow, just do one division.
+ */
+ ullQuotient = RedUint64DivMod32(ullDividend, (uint32_t)ullDivisor, NULL);
+ }
+ else
+ {
+ uint32_t k;
+
+ /* If ullDividend/ullDivisor would overflow:
+ */
+
+ /* Break ullDividend up into two halves.
+ */
+ u1 = ullDividend >> 32U;
+ u0 = ullDividend & 0xFFFFFFFFU;
+
+ /* First quotient digit and first remainder.
+ */
+ q1 = RedUint64DivMod32(u1, (uint32_t)ullDivisor, &k);
+
+ /* 2nd quot. digit.
+ */
+ q0 = RedUint64DivMod32(((uint64_t)k << 32U) + u0, (uint32_t)ullDivisor, NULL);
+
+ ullQuotient = (q1 << 32U) + q0;
+ }
+ }
+ else
+ {
+ uint64_t n;
+ uint64_t v1;
+
+ n = nlz64(ullDivisor); /* 0 <= n <= 31. */
+ v1 = (ullDivisor << n) >> 32U; /* Normalize the divisor so its MSB is 1. */
+ u1 = ullDividend >> 1U; /* To ensure no overflow. */
+
+ /* Get quotient from divide unsigned insn.
+ */
+ q1 = RedUint64DivMod32(u1, (uint32_t)v1, NULL);
+
+ q0 = (q1 << n) >> 31U; /* Undo normalization and division of ullDividend by 2. */
+
+ /* Make q0 correct or too small by 1.
+ */
+ if(q0 != 0U)
+ {
+ q0--;
+ }
+
+ if((ullDividend - (q0 * ullDivisor)) >= ullDivisor)
+ {
+ q0++; /* Now q0 is correct. */
+ }
+
+ ullQuotient = q0;
+ }
+
+ if(pullRemainder != NULL)
+ {
+ *pullRemainder = ullDividend - (ullQuotient * ullDivisor);
+ }
+
+ return ullQuotient;
+}
+
+
+/** @brief Compute the number of leading zeroes in a 64-bit value.
+
+ @param ullValue The value for which to compute the NLZ.
+
+ @return The number of leading zeroes in @p ullValue.
+*/
+static uint32_t nlz64(
+ uint64_t ullValue)
+{
+ uint32_t n;
+
+ if(ullValue == 0U)
+ {
+ n = 64U;
+ }
+ else
+ {
+ uint64_t x = ullValue;
+
+ n = 0U;
+
+ if(x <= UINT64_SUFFIX(0x00000000FFFFFFFF))
+ {
+ n += 32U;
+ x <<= 32U;
+ }
+
+ if(x <= UINT64_SUFFIX(0x0000FFFFFFFFFFFF))
+ {
+ n += 16U;
+ x <<= 16U;
+ }
+
+ if(x <= UINT64_SUFFIX(0x00FFFFFFFFFFFFFF))
+ {
+ n += 8U;
+ x <<= 8U;
+ }
+
+ if(x <= UINT64_SUFFIX(0x0FFFFFFFFFFFFFFF))
+ {
+ n += 4U;
+ x <<= 4U;
+ }
+
+ if(x <= UINT64_SUFFIX(0x3FFFFFFFFFFFFFFF))
+ {
+ n += 2U;
+ x <<= 2U;
+ }
+
+ if(x <= UINT64_SUFFIX(0x7FFFFFFFFFFFFFFF))
+ {
+ n += 1;
+ }
+ }
+
+ return n;
+}
+
-/* ----> DO NOT REMOVE THE FOLLOWING NOTICE <----\r
-\r
- Copyright (c) 2014-2015 Datalight, Inc.\r
- All Rights Reserved Worldwide.\r
-\r
- This program is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; use version 2 of the License.\r
-\r
- This program is distributed in the hope that it will be useful,\r
- but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty\r
- of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License along\r
- with this program; if not, write to the Free Software Foundation, Inc.,\r
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
-*/\r
-/* Businesses and individuals that for commercial or other reasons cannot\r
- comply with the terms of the GPLv2 license may obtain a commercial license\r
- before incorporating Reliance Edge into proprietary software for\r
- distribution in any form. Visit http://www.datalight.com/reliance-edge for\r
- more information.\r
-*/\r
-/** @file\r
- @brief Implements functions for printing.\r
-\r
- These functions are intended to be used in portable test code, which cannot\r
- assume the standard I/O functions will be available. Similar to their ANSI\r
- C counterparts, these functions allow formatting text strings and (if the\r
- configuration allows it) outputing formatted text. The latter ability\r
- relies on the RedOsOutputString() OS service function.\r
-\r
- Do *not* use these functions in code which can safely assume the standard\r
- I/O functions are available (e.g., in host tools code).\r
-\r
- Do *not* use these functions from within the file system driver. These\r
- functions use variable arguments and thus are not MISRA-C:2012 compliant.\r
-*/\r
-#include <redfs.h>\r
-#include <redtestutils.h>\r
-#include <limits.h>\r
-#include <stdarg.h>\r
-\r
-\r
-/** @brief Maximum number of bytes of output supported by RedPrintf().\r
-\r
- Typically only Datalight code uses these functions, and that could should be\r
- written to respect this limit, so it should not normally be necessary to\r
- adjust this value.\r
-*/\r
-#define OUTPUT_BUFFER_SIZE 256U\r
-\r
-\r
-typedef enum\r
-{\r
- PRFMT_UNKNOWN = 0,\r
- PRFMT_CHAR,\r
- PRFMT_ANSISTRING,\r
- PRFMT_SIGNED8BIT,\r
- PRFMT_UNSIGNED8BIT,\r
- PRFMT_SIGNED16BIT,\r
- PRFMT_UNSIGNED16BIT,\r
- PRFMT_SIGNED32BIT,\r
- PRFMT_UNSIGNED32BIT,\r
- PRFMT_SIGNED64BIT,\r
- PRFMT_UNSIGNED64BIT,\r
- PRFMT_HEX8BIT,\r
- PRFMT_HEX16BIT,\r
- PRFMT_HEX32BIT,\r
- PRFMT_HEX64BIT,\r
- PRFMT_POINTER,\r
- PRFMT_DOUBLEPERCENT\r
-} PRINTTYPE;\r
-\r
-typedef struct\r
-{\r
- PRINTTYPE type; /* The PRFMT_* type found */\r
- uint32_t ulSpecifierIdx; /* Returns a pointer to the % sign */\r
- uint32_t ulFillLen;\r
- char cFillChar;\r
- bool fLeftJustified;\r
- bool fHasIllegalType; /* TRUE if an illegal sequence was skipped over */\r
- bool fHasVarWidth;\r
-} PRINTFORMAT;\r
-\r
-\r
-/* Our output handlers are written for standard fixed width data types. Map\r
- the standard ANSI C data types onto our handlers. Currently this code has\r
- the following requirements:\r
-\r
- 1) shorts must be either 16 or 32 bits\r
- 2) ints must be either 16 or 32 bits\r
- 3) longs must be between 32 or 64 bits\r
- 4) long longs must be 64 bits\r
-*/\r
-#if (USHRT_MAX == 0xFFFFU)\r
- #define MAPSHORT PRFMT_SIGNED16BIT\r
- #define MAPUSHORT PRFMT_UNSIGNED16BIT\r
- #define MAPHEXUSHORT PRFMT_HEX16BIT\r
-#elif (USHRT_MAX == 0xFFFFFFFFU)\r
- #define MAPSHORT PRFMT_SIGNED32BIT\r
- #define MAPUSHORT PRFMT_UNSIGNED32BIT\r
- #define MAPHEXUSHORT PRFMT_HEX32BIT\r
-#else\r
- #error "The 'short' data type does not have a 16 or 32-bit width"\r
-#endif\r
-\r
-#if (UINT_MAX == 0xFFFFU)\r
- #define MAPINT PRFMT_SIGNED16BIT\r
- #define MAPUINT PRFMT_UNSIGNED16BIT\r
- #define MAPHEXUINT PRFMT_HEX16BIT\r
-#elif (UINT_MAX == 0xFFFFFFFFU)\r
- #define MAPINT PRFMT_SIGNED32BIT\r
- #define MAPUINT PRFMT_UNSIGNED32BIT\r
- #define MAPHEXUINT PRFMT_HEX32BIT\r
-#else\r
- #error "The 'int' data type does not have a 16 or 32-bit width"\r
-#endif\r
-\r
-#if (ULONG_MAX == 0xFFFFFFFFU)\r
- #define MAPLONG PRFMT_SIGNED32BIT\r
- #define MAPULONG PRFMT_UNSIGNED32BIT\r
- #define MAPHEXULONG PRFMT_HEX32BIT\r
-#elif (ULONG_MAX <= UINT64_SUFFIX(0xFFFFFFFFFFFFFFFF))\r
- /* We've run into unusual environments where "longs" are 40-bits wide.\r
- In this event, map them to 64-bit types so no data is lost.\r
- */\r
- #define MAPLONG PRFMT_SIGNED64BIT\r
- #define MAPULONG PRFMT_UNSIGNED64BIT\r
- #define MAPHEXULONG PRFMT_HEX64BIT\r
-#else\r
- #error "The 'long' data type is not between 32 and 64 bits wide"\r
-#endif\r
-\r
-#if defined(ULLONG_MAX) && (ULLONG_MAX != UINT64_SUFFIX(0xFFFFFFFFFFFFFFFF))\r
- #error "The 'long long' data type is not 64 bits wide"\r
-#else\r
- #define MAPLONGLONG PRFMT_SIGNED64BIT\r
- #define MAPULONGLONG PRFMT_UNSIGNED64BIT\r
- #define MAPHEXULONGLONG PRFMT_HEX64BIT\r
-#endif\r
-\r
-\r
-static uint32_t ProcessFormatSegment(char *pcBuffer, uint32_t ulBufferLen, const char *pszFormat, PRINTFORMAT *pFormat, uint32_t *pulSpecifierLen);\r
-static uint32_t ParseFormatSpecifier(char const *pszFomat, PRINTFORMAT *pFormatType);\r
-static PRINTTYPE ParseFormatType(const char *pszFormat, uint32_t *pulTypeLen);\r
-static uint32_t LtoA(char *pcBuffer, uint32_t ulBufferLen, int32_t lNum, uint32_t ulFillLen, char cFill);\r
-static uint32_t LLtoA(char *pcBuffer, uint32_t ulBufferLen, int64_t llNum, uint32_t ulFillLen, char cFill);\r
-static uint32_t ULtoA(char *pcBuffer, uint32_t ulBufferLen, uint32_t ulNum, bool fHex, uint32_t ulFillLen, char cFill);\r
-static uint32_t ULLtoA(char *pcBuffer, uint32_t ulBufferLen, uint64_t ullNum, bool fHex, uint32_t ulFillLen, char cFill);\r
-static uint32_t FinishToA(const char *pcDigits, uint32_t ulDigits, char *pcOutBuffer, uint32_t ulBufferLen, uint32_t ulFillLen, char cFill);\r
-\r
-\r
-/* Digits for the *LtoA() routines.\r
-*/\r
-static const char gacDigits[] = "0123456789ABCDEF";\r
-\r
-\r
-#if REDCONF_OUTPUT == 1\r
-/** @brief Print formatted data with a variable length argument list.\r
-\r
- This function provides a subset of the ANSI C printf() functionality with\r
- several extensions to support fixed size data types.\r
-\r
- See RedVSNPrintf() for the list of supported types.\r
-\r
- @param pszFormat A pointer to the null-terminated format string.\r
- @param ... The variable length argument list.\r
-*/\r
-void RedPrintf(\r
- const char *pszFormat,\r
- ...)\r
-{\r
- va_list arglist;\r
-\r
- va_start(arglist, pszFormat);\r
-\r
- RedVPrintf(pszFormat, arglist);\r
-\r
- va_end(arglist);\r
-}\r
-\r
-\r
-/** @brief Print formatted data using a pointer to a variable length argument\r
- list.\r
-\r
- This function provides a subset of the ANSI C vprintf() functionality.\r
-\r
- See RedVSNPrintf() for the list of supported types.\r
-\r
- This function accommodates a maximum output length of #OUTPUT_BUFFER_SIZE.\r
- If this function must truncate the output, and the original string was\r
- \n terminated, the truncated output will be \n terminated as well.\r
-\r
- @param pszFormat A pointer to the null-terminated format string.\r
- @param arglist The variable length argument list.\r
-*/\r
-void RedVPrintf(\r
- const char *pszFormat,\r
- va_list arglist)\r
-{\r
- char achBuffer[OUTPUT_BUFFER_SIZE];\r
-\r
- if(RedVSNPrintf(achBuffer, sizeof(achBuffer), pszFormat, arglist) == -1)\r
- {\r
- /* Ensture the buffer is null terminated.\r
- */\r
- achBuffer[sizeof(achBuffer) - 1U] = '\0';\r
-\r
- /* If the original string was \n terminated and the new one is not, due to\r
- truncation, stuff a \n into the new one.\r
- */\r
- if(pszFormat[RedStrLen(pszFormat) - 1U] == '\n')\r
- {\r
- achBuffer[sizeof(achBuffer) - 2U] = '\n';\r
- }\r
- }\r
-\r
- RedOsOutputString(achBuffer);\r
-}\r
-#endif /* #if REDCONF_OUTPUT == 1 */\r
-\r
-\r
-/** @brief Format arguments into a string using a subset of the ANSI C\r
- vsprintf() functionality.\r
-\r
- This function is modeled after the Microsoft _snprint() extension to the\r
- ANSI C sprintf() function, and allows a buffer length to be specified so\r
- that overflow is avoided.\r
-\r
- See RedVSNPrintf() for the list of supported types.\r
-\r
- @param pcBuffer A pointer to the output buffer\r
- @param ulBufferLen The output buffer length\r
- @param pszFormat A pointer to the null terminated format string\r
- @param ... Variable argument list\r
-\r
- @return The length output, or -1 if the buffer filled up. If -1 is\r
- returned, the output buffer may not be null-terminated.\r
-*/\r
-int32_t RedSNPrintf(\r
- char *pcBuffer,\r
- uint32_t ulBufferLen,\r
- const char *pszFormat,\r
- ...)\r
-{\r
- int32_t iLen;\r
- va_list arglist;\r
-\r
- va_start(arglist, pszFormat);\r
-\r
- iLen = RedVSNPrintf(pcBuffer, ulBufferLen, pszFormat, arglist);\r
-\r
- va_end(arglist);\r
-\r
- return iLen;\r
-}\r
-\r
-\r
-/** @brief Format arguments into a string using a subset of the ANSI C\r
- vsprintf() functionality.\r
-\r
- This function is modeled after the Microsoft _vsnprint() extension to the\r
- ANSI C vsprintf() function, and requires a buffer length to be specified so\r
- that overflow is avoided.\r
-\r
- The following ANSI C standard formatting codes are supported:\r
-\r
- | Code | Meaning |\r
- | ---- | ---------------------------------- |\r
- | %c | Format a character |\r
- | %s | Format a null-terminated C string |\r
- | %hd | Format a signed short |\r
- | %hu | Format an unsigned short |\r
- | %d | Format a signed integer |\r
- | %u | Format an unsigned integer |\r
- | %ld | Format a signed long |\r
- | %lu | Format an unsigned long |\r
- | %lld | Format a signed long long |\r
- | %llu | Format an unsigned long long |\r
- | %hx | Format a short in hex |\r
- | %x | Format an integer in hex |\r
- | %lx | Format a long in hex |\r
- | %llx | Format a long long in hex |\r
- | %p | Format a pointer (hex value) |\r
-\r
- @note All formatting codes are case-sensitive.\r
-\r
- Fill characters and field widths are supported per the ANSI standard, as is\r
- left justification with the '-' character.\r
-\r
- The only supported fill characters are '0', ' ', and '_'.\r
-\r
- '*' is supported to specify variable length field widths.\r
-\r
- Hexidecimal numbers are always displayed in upper case. Formatting codes\r
- which specifically request upper case (e.g., "%lX") are not supported.\r
-\r
- Unsupported behaviors:\r
- - Precision is not supported.\r
- - Floating point is not supported.\r
-\r
- Errata:\r
- - There is a subtle difference in the return value for this function versus\r
- the Microsoft implementation. In the Microsoft version, if the buffer\r
- exactly fills up, but there is no room for a null-terminator, the return\r
- value will be the length of the buffer. In this code, -1 will be returned\r
- when this happens.\r
- - When using left justified strings, the only supported fill character is a\r
- space, regardless of what may be specified. It is not clear if this is\r
- ANSI standard or just the way the Microsoft function works, but we emulate\r
- the Microsoft behavior.\r
-\r
- @param pcBuffer A pointer to the output buffer.\r
- @param ulBufferLen The output buffer length.\r
- @param pszFormat A pointer to the null terminated ANSI format string.\r
- @param arglist Variable argument list.\r
-\r
- @return The length output, or -1 if the buffer filled up. If -1 is\r
- returned, the output buffer may not be null-terminated.\r
-*/\r
-int32_t RedVSNPrintf(\r
- char *pcBuffer,\r
- uint32_t ulBufferLen,\r
- const char *pszFormat,\r
- va_list arglist)\r
-{\r
- uint32_t ulBufIdx = 0U;\r
- uint32_t ulFmtIdx = 0U;\r
- int32_t iLen;\r
-\r
- while((pszFormat[ulFmtIdx] != '\0') && (ulBufIdx < ulBufferLen))\r
- {\r
- PRINTFORMAT fmt;\r
- uint32_t ulSpecifierLen;\r
- uint32_t ulWidth;\r
-\r
- /* Process the next segment of the format string, outputting\r
- any non-format specifiers, as output buffer space allows,\r
- and return information about the next format specifier.\r
- */\r
- ulWidth = ProcessFormatSegment(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, &pszFormat[ulFmtIdx], &fmt, &ulSpecifierLen);\r
- if(ulWidth)\r
- {\r
- REDASSERT(ulWidth <= (ulBufferLen - ulBufIdx));\r
-\r
- ulBufIdx += ulWidth;\r
- }\r
-\r
- /* If no specifier was found, or if the output buffer is\r
- full, we're done -- get out.\r
- */\r
- if((ulSpecifierLen == 0U) || (ulBufIdx == ulBufferLen))\r
- {\r
- break;\r
- }\r
-\r
- /* Otherwise, the math should add up for these things...\r
- */\r
- REDASSERT(&pszFormat[fmt.ulSpecifierIdx] == &pszFormat[ulWidth]);\r
-\r
- /* Point past the specifier, to the next piece of the format string.\r
- */\r
- ulFmtIdx = ulFmtIdx + fmt.ulSpecifierIdx + ulSpecifierLen;\r
-\r
- if(fmt.fHasVarWidth)\r
- {\r
- int iFillLen = va_arg(arglist, int);\r
-\r
- if(iFillLen >= 0)\r
- {\r
- fmt.ulFillLen = (uint32_t)iFillLen;\r
- }\r
- else\r
- {\r
- /* Bogus fill length. Ignore.\r
- */\r
- fmt.ulFillLen = 0U;\r
- }\r
- }\r
-\r
- switch(fmt.type)\r
- {\r
- case PRFMT_DOUBLEPERCENT:\r
- {\r
- /* Nothing to do. A single percent has already been output,\r
- and we just finished skipping past the second percent.\r
- */\r
- break;\r
- }\r
-\r
- /*-----------------> Small int handling <------------------\r
- *\r
- * Values smaller than "int" will be promoted to "int" by\r
- * the compiler, so we must retrieve them using "int" when\r
- * calling va_arg(). Once we've done that, we immediately\r
- * put the value into the desired data type.\r
- *---------------------------------------------------------*/\r
-\r
- case PRFMT_CHAR:\r
- {\r
- pcBuffer[ulBufIdx] = (char)va_arg(arglist, int);\r
- ulBufIdx++;\r
- break;\r
- }\r
- case PRFMT_SIGNED8BIT:\r
- {\r
- int8_t num = (int8_t)va_arg(arglist, int);\r
-\r
- ulBufIdx += LtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, num, fmt.ulFillLen, fmt.cFillChar);\r
- break;\r
- }\r
- case PRFMT_UNSIGNED8BIT:\r
- {\r
- uint8_t bNum = (uint8_t)va_arg(arglist, unsigned);\r
-\r
- ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, bNum, false, fmt.ulFillLen, fmt.cFillChar);\r
- break;\r
- }\r
- case PRFMT_HEX8BIT:\r
- {\r
- uint8_t bNum = (uint8_t)va_arg(arglist, unsigned);\r
-\r
- ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, bNum, true, fmt.ulFillLen, fmt.cFillChar);\r
- break;\r
- }\r
- case PRFMT_SIGNED16BIT:\r
- {\r
- int16_t num = (int16_t)va_arg(arglist, int);\r
-\r
- ulBufIdx += LtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, num, fmt.ulFillLen, fmt.cFillChar);\r
- break;\r
- }\r
- case PRFMT_UNSIGNED16BIT:\r
- {\r
- uint16_t uNum = (uint16_t)va_arg(arglist, unsigned);\r
-\r
- ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, uNum, false, fmt.ulFillLen, fmt.cFillChar);\r
- break;\r
- }\r
- case PRFMT_HEX16BIT:\r
- {\r
- uint16_t uNum = (uint16_t)va_arg(arglist, unsigned);\r
-\r
- ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, uNum, true, fmt.ulFillLen, fmt.cFillChar);\r
- break;\r
- }\r
- case PRFMT_SIGNED32BIT:\r
- {\r
- int32_t lNum = va_arg(arglist, int32_t);\r
-\r
- ulBufIdx += LtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, lNum, fmt.ulFillLen, fmt.cFillChar);\r
- break;\r
- }\r
- case PRFMT_UNSIGNED32BIT:\r
- {\r
- uint32_t ulNum = va_arg(arglist, uint32_t);\r
-\r
- ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, ulNum, false, fmt.ulFillLen, fmt.cFillChar);\r
- break;\r
- }\r
- case PRFMT_HEX32BIT:\r
- {\r
- uint32_t ulNum = va_arg(arglist, uint32_t);\r
-\r
- ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, ulNum, true, fmt.ulFillLen, fmt.cFillChar);\r
- break;\r
- }\r
- case PRFMT_SIGNED64BIT:\r
- {\r
- int64_t llNum = va_arg(arglist, int64_t);\r
-\r
- ulBufIdx += LLtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, llNum, fmt.ulFillLen, fmt.cFillChar);\r
- break;\r
- }\r
- case PRFMT_UNSIGNED64BIT:\r
- {\r
- uint64_t ullNum = va_arg(arglist, uint64_t);\r
-\r
- ulBufIdx += ULLtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, ullNum, false, fmt.ulFillLen, fmt.cFillChar);\r
- break;\r
- }\r
- case PRFMT_HEX64BIT:\r
- {\r
- uint64_t ullNum = va_arg(arglist, uint64_t);\r
-\r
- ulBufIdx += ULLtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, ullNum, true, fmt.ulFillLen, fmt.cFillChar);\r
- break;\r
- }\r
- case PRFMT_POINTER:\r
- {\r
- const void *ptr = va_arg(arglist, const void *);\r
-\r
- /* Assert our assumption.\r
- */\r
- REDASSERT(sizeof(void *) <= 8U);\r
-\r
- /* Format as either a 64-bit or a 32-bit value.\r
- */\r
- if(sizeof(void *) > 4U)\r
- {\r
- /* Attempt to quiet warnings.\r
- */\r
- uintptr_t ptrval = (uintptr_t)ptr;\r
- uint64_t ullPtrVal = (uint64_t)ptrval;\r
-\r
- ulBufIdx += ULLtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, ullPtrVal, true, fmt.ulFillLen, fmt.cFillChar);\r
- }\r
- else\r
- {\r
- /* Attempt to quiet warnings.\r
- */\r
- uintptr_t ptrval = (uintptr_t)ptr;\r
- uint32_t ulPtrVal = (uint32_t)ptrval;\r
-\r
- ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, ulPtrVal, true, fmt.ulFillLen, fmt.cFillChar);\r
- }\r
-\r
- break;\r
- }\r
- case PRFMT_ANSISTRING:\r
- {\r
- const char *pszArg = va_arg(arglist, const char *);\r
- uint32_t ulArgIdx = 0U;\r
-\r
- if(pszArg == NULL)\r
- {\r
- pszArg = "null";\r
- }\r
-\r
- if(fmt.ulFillLen > 0U)\r
- {\r
- if(!fmt.fLeftJustified)\r
- {\r
- uint32_t ulLen = RedStrLen(pszArg);\r
-\r
- /* So long as we are not left justifying, fill as many\r
- characters as is necessary to make the string right\r
- justified.\r
- */\r
- while(((ulBufferLen - ulBufIdx) > 0U) && (fmt.ulFillLen > ulLen))\r
- {\r
- pcBuffer[ulBufIdx] = fmt.cFillChar;\r
- ulBufIdx++;\r
- fmt.ulFillLen--;\r
- }\r
- }\r
-\r
- /* Move as many characters as we have space for into the\r
- output buffer.\r
- */\r
- while(((ulBufferLen - ulBufIdx) > 0U) && (pszArg[ulArgIdx] != '\0'))\r
- {\r
- pcBuffer[ulBufIdx] = pszArg[ulArgIdx];\r
- ulBufIdx++;\r
- ulArgIdx++;\r
- if(fmt.ulFillLen > 0U)\r
- {\r
- fmt.ulFillLen--;\r
- }\r
- }\r
-\r
- /* If there is any space left to fill, do it (the string\r
- must have been left justified).\r
- */\r
- while(((ulBufferLen - ulBufIdx) > 0U) && (fmt.ulFillLen > 0U))\r
- {\r
- /* This is NOT a typo -- when using left justified\r
- strings, spaces are the only allowed fill character.\r
- See the errata.\r
- */\r
- pcBuffer[ulBufIdx] = ' ';\r
- ulBufIdx++;\r
- fmt.ulFillLen--;\r
- }\r
- }\r
- else\r
- {\r
- /* No fill characters, just move up to as many\r
- characters as we have space for in the output\r
- buffer.\r
- */\r
- while(((ulBufferLen - ulBufIdx) > 0U) && (pszArg[ulArgIdx] != '\0'))\r
- {\r
- pcBuffer[ulBufIdx] = pszArg[ulArgIdx];\r
- ulBufIdx++;\r
- ulArgIdx++;\r
- }\r
- }\r
- break;\r
- }\r
- default:\r
- {\r
- REDERROR();\r
- break;\r
- }\r
- }\r
- }\r
-\r
- /* If there is space, tack on a null and return the output length\r
- processed, not including the null.\r
- */\r
- if(ulBufIdx < ulBufferLen)\r
- {\r
- pcBuffer[ulBufIdx] = '\0';\r
- iLen = (int32_t)ulBufIdx;\r
- }\r
- else\r
- {\r
- /* Not enough space, just return -1, with no null termination\r
- */\r
- iLen = -1;\r
- }\r
-\r
- return iLen;\r
-}\r
-\r
-\r
-/** @brief Process the next segment of the format string, outputting any\r
- non-format specifiers, as output buffer space allows, and return\r
- information about the next format specifier.\r
-\r
- @note If the returned value is the same as the supplied @p ulBufferLen,\r
- the output buffer will not be null-terminated. In all other cases,\r
- the result will be null-terminated. The returned length will never\r
- include the null in the count.\r
-\r
- @param pcBuffer The output buffer.\r
- @param ulBufferLen The output buffer length.\r
- @param pszFormat The format string to process.\r
- @param pFormat The PRINTFORMAT structure to fill.\r
- @param pulSpecifierLen Returns the length of any format specifier string,\r
- or zero if no specifier was found.\r
-\r
- @return The count of characters from pszFormatt which were processed and\r
- copied to pcBuffer.\r
- - If zero is returned and *pulSpecifierLen is non-zero, then\r
- a format specifier string was found at the start of pszFmt.\r
- - If non-zero is returned and *pulSpecifierLen is zero, then\r
- no format specifier string was found, and the entire pszFmt\r
- string was copied to pBuffer (or as much as will fit).\r
-*/\r
-static uint32_t ProcessFormatSegment(\r
- char *pcBuffer,\r
- uint32_t ulBufferLen,\r
- const char *pszFormat,\r
- PRINTFORMAT *pFormat,\r
- uint32_t *pulSpecifierLen)\r
-{\r
- uint32_t ulWidth = 0U;\r
-\r
- /* Find the next format specifier string, and information about it.\r
- */\r
- *pulSpecifierLen = ParseFormatSpecifier(pszFormat, pFormat);\r
-\r
- if(*pulSpecifierLen == 0U)\r
- {\r
- /* If no specifier was found at all, then simply output the full length\r
- of the string, or as much as will fit.\r
- */\r
- ulWidth = REDMIN(ulBufferLen, RedStrLen(pszFormat));\r
-\r
- RedMemCpy(pcBuffer, pszFormat, ulWidth);\r
- }\r
- else\r
- {\r
- /* If we encountered a double percent, skip past one of them so it is\r
- copied into the output buffer.\r
- */\r
- if(pFormat->type == PRFMT_DOUBLEPERCENT)\r
- {\r
- pFormat->ulSpecifierIdx++;\r
-\r
- /* A double percent specifier always has a length of two. Since\r
- we're processing one of those percent signs, reduce the length\r
- to one. Assert it so.\r
- */\r
- REDASSERT(*pulSpecifierLen == 2U);\r
-\r
- (*pulSpecifierLen)--;\r
- }\r
-\r
- /* So long as the specifier is not the very first thing in the format\r
- string...\r
- */\r
- if(pFormat->ulSpecifierIdx != 0U)\r
- {\r
- /* A specifier was found, but there is other data preceding it.\r
- Copy as much as allowed to the output buffer.\r
- */\r
- ulWidth = REDMIN(ulBufferLen, pFormat->ulSpecifierIdx);\r
-\r
- RedMemCpy(pcBuffer, pszFormat, ulWidth);\r
- }\r
- }\r
-\r
- /* If there is room in the output buffer, null-terminate whatever is there.\r
- But note that the returned length never includes the null.\r
- */\r
- if(ulWidth < ulBufferLen)\r
- {\r
- pcBuffer[ulWidth] = 0U;\r
- }\r
-\r
- return ulWidth;\r
-}\r
-\r
-\r
-/** @brief Parse the specified format string for a valid RedVSNPrintf() format\r
- sequence, and return information about it.\r
-\r
- @param pszFormat The format string to process.\r
- @param pFormatType The PRINTFORMAT structure to fill. The data is only\r
- valid if a non-zero length is returned.\r
-\r
- @return The length of the full format specifier string, starting at\r
- pFormat->ulSpecifierIdx. Returns zero if a valid specifier was\r
- not found.\r
-*/\r
-static uint32_t ParseFormatSpecifier(\r
- char const *pszFomat,\r
- PRINTFORMAT *pFormatType)\r
-{\r
- bool fContainsIllegalSequence = false;\r
- uint32_t ulLen = 0U;\r
- uint32_t ulIdx = 0U;\r
-\r
- while(pszFomat[ulIdx] != '\0')\r
- {\r
- uint32_t ulTypeLen;\r
-\r
- /* general output\r
- */\r
- if(pszFomat[ulIdx] != '%')\r
- {\r
- ulIdx++;\r
- }\r
- else\r
- {\r
- RedMemSet(pFormatType, 0U, sizeof(*pFormatType));\r
-\r
- /* Record the location of the start of the format sequence\r
- */\r
- pFormatType->ulSpecifierIdx = ulIdx;\r
- ulIdx++;\r
-\r
- if(pszFomat[ulIdx] == '-')\r
- {\r
- pFormatType->fLeftJustified = true;\r
- ulIdx++;\r
- }\r
-\r
- if((pszFomat[ulIdx] == '0') || (pszFomat[ulIdx] == '_'))\r
- {\r
- pFormatType->cFillChar = pszFomat[ulIdx];\r
- ulIdx++;\r
- }\r
- else\r
- {\r
- pFormatType->cFillChar = ' ';\r
- }\r
-\r
- if(pszFomat[ulIdx] == '*')\r
- {\r
- pFormatType->fHasVarWidth = true;\r
- ulIdx++;\r
- }\r
- else if(ISDIGIT(pszFomat[ulIdx]))\r
- {\r
- pFormatType->ulFillLen = (uint32_t)RedAtoI(&pszFomat[ulIdx]);\r
- while(ISDIGIT(pszFomat[ulIdx]))\r
- {\r
- ulIdx++;\r
- }\r
- }\r
- else\r
- {\r
- /* No fill length.\r
- */\r
- }\r
-\r
- pFormatType->type = ParseFormatType(&pszFomat[ulIdx], &ulTypeLen);\r
- if(pFormatType->type != PRFMT_UNKNOWN)\r
- {\r
- /* Even though we are returning successfully, keep track of\r
- whether an illegal sequence was encountered and skipped.\r
- */\r
- pFormatType->fHasIllegalType = fContainsIllegalSequence;\r
-\r
- ulLen = (ulIdx - pFormatType->ulSpecifierIdx) + ulTypeLen;\r
- break;\r
- }\r
-\r
- /* In the case of an unrecognized type string, simply ignore\r
- it entirely. Reset the pointer to the position following\r
- the percent sign, so it is not found again.\r
- */\r
- fContainsIllegalSequence = false;\r
- ulIdx = pFormatType->ulSpecifierIdx + 1U;\r
- }\r
- }\r
-\r
- return ulLen;\r
-}\r
-\r
-\r
-/** @brief Parse a RedPrintf() format type string to determine the proper data\r
- type.\r
-\r
- @param pszFormat The format string to process. This must be a pointer to\r
- the character following any width or justification\r
- characters.\r
- @param pulTypeLen The location in which to store the type length. The\r
- value will be 0 if PRFMT_UNKNOWN is returned.\r
-\r
- @return Rhe PRFMT_* type value, or PRFMT_UNKNOWN if the type is not\r
- recognized.\r
-*/\r
-static PRINTTYPE ParseFormatType(\r
- const char *pszFormat,\r
- uint32_t *pulTypeLen)\r
-{\r
- PRINTTYPE fmtType = PRFMT_UNKNOWN;\r
- uint32_t ulIdx = 0U;\r
-\r
- switch(pszFormat[ulIdx])\r
- {\r
- case '%':\r
- fmtType = PRFMT_DOUBLEPERCENT;\r
- break;\r
- case 'c':\r
- fmtType = PRFMT_CHAR;\r
- break;\r
- case 's':\r
- fmtType = PRFMT_ANSISTRING;\r
- break;\r
- case 'p':\r
- fmtType = PRFMT_POINTER;\r
- break;\r
- case 'd':\r
- fmtType = MAPINT;\r
- break;\r
- case 'u':\r
- fmtType = MAPUINT;\r
- break;\r
- case 'x':\r
- fmtType = MAPHEXUINT;\r
- break;\r
- case 'h':\r
- {\r
- ulIdx++;\r
- switch(pszFormat[ulIdx])\r
- {\r
- case 'd':\r
- fmtType = MAPSHORT;\r
- break;\r
- case 'u':\r
- fmtType = MAPUSHORT;\r
- break;\r
- case 'x':\r
- fmtType = MAPHEXUSHORT;\r
- break;\r
- default:\r
- break;\r
- }\r
- break;\r
- }\r
- case 'l':\r
- {\r
- ulIdx++;\r
- switch(pszFormat[ulIdx])\r
- {\r
- case 'd':\r
- fmtType = MAPLONG;\r
- break;\r
- case 'u':\r
- fmtType = MAPULONG;\r
- break;\r
- case 'x':\r
- fmtType = MAPHEXULONG;\r
- break;\r
- case 'l':\r
- {\r
- ulIdx++;\r
- switch(pszFormat[ulIdx])\r
- {\r
- case 'd':\r
- fmtType = MAPLONGLONG;\r
- break;\r
- case 'u':\r
- fmtType = MAPULONGLONG;\r
- break;\r
- case 'x':\r
- case 'X':\r
- fmtType = MAPHEXULONGLONG;\r
- break;\r
- default:\r
- break;\r
- }\r
- break;\r
- }\r
- default:\r
- break;\r
- }\r
- break;\r
- }\r
- default:\r
- break;\r
- }\r
-\r
- if(fmtType != PRFMT_UNKNOWN)\r
- {\r
- *pulTypeLen = ulIdx + 1U;\r
- }\r
- else\r
- {\r
- *pulTypeLen = 0U;\r
- }\r
-\r
- return fmtType;\r
-}\r
-\r
-\r
-/** @brief Format a signed 32-bit integer as a base 10 ASCII string.\r
-\r
- @note If the output buffer length is exhausted, the result will *not* be\r
- null-terminated.\r
-\r
- @note If the @p ulFillLen value is greater than or equal to the buffer\r
- length, the result will not be null-terminated, even if the\r
- formatted portion of the data is shorter than the buffer length.\r
-\r
- @param pcBuffer The output buffer\r
- @param ulBufferLen A pointer to the output buffer length\r
- @param lNum The 32-bit signed number to convert\r
- @param ulFillLen The fill length, if any\r
- @param cFill The fill character to use\r
-\r
- @return The length of the string.\r
-*/\r
-static uint32_t LtoA(\r
- char *pcBuffer,\r
- uint32_t ulBufferLen,\r
- int32_t lNum,\r
- uint32_t ulFillLen,\r
- char cFill)\r
-{\r
- uint32_t ulLen;\r
-\r
- if(pcBuffer == NULL)\r
- {\r
- REDERROR();\r
- ulLen = 0U;\r
- }\r
- else\r
- {\r
- char ach[12U]; /* big enough for a int32_t in base 10 */\r
- uint32_t ulDigits = 0U;\r
- uint32_t ulNum;\r
- bool fSign;\r
-\r
- if(lNum < 0)\r
- {\r
- fSign = true;\r
- ulNum = (uint32_t)-lNum;\r
- }\r
- else\r
- {\r
- fSign = false;\r
- ulNum = (uint32_t)lNum;\r
- }\r
-\r
- do\r
- {\r
- ach[ulDigits] = gacDigits[ulNum % 10U];\r
- ulNum = ulNum / 10U;\r
- ulDigits++;\r
- }\r
- while(ulNum);\r
-\r
- if(fSign)\r
- {\r
- ach[ulDigits] = '-';\r
- ulDigits++;\r
- }\r
-\r
- ulLen = FinishToA(ach, ulDigits, pcBuffer, ulBufferLen, ulFillLen, cFill);\r
- }\r
-\r
- return ulLen;\r
-}\r
-\r
-\r
-/** @brief Format a signed 64-bit integer as a base 10 ASCII string.\r
-\r
- @note If the output buffer length is exhausted, the result will *not* be\r
- null-terminated.\r
-\r
- @note If the @p ulFillLen value is greater than or equal to the buffer\r
- length, the result will not be null-terminated, even if the\r
- formatted portion of the data is shorter than the buffer length.\r
-\r
- @param pcBuffer The output buffer\r
- @param ulBufferLen A pointer to the output buffer length\r
- @param llNum The 64-bit signed number to convert\r
- @param ulFillLen The fill length, if any\r
- @param cFill The fill character to use\r
-\r
- @return The length of the string.\r
-*/\r
-static uint32_t LLtoA(\r
- char *pcBuffer,\r
- uint32_t ulBufferLen,\r
- int64_t llNum,\r
- uint32_t ulFillLen,\r
- char cFill)\r
-{\r
- uint32_t ulLen;\r
-\r
- if(pcBuffer == NULL)\r
- {\r
- REDERROR();\r
- ulLen = 0U;\r
- }\r
- else\r
- {\r
- char ach[12U]; /* big enough for a int32_t in base 10 */\r
- uint32_t ulDigits = 0U;\r
- uint64_t ullNum;\r
- bool fSign;\r
-\r
- if(llNum < 0)\r
- {\r
- fSign = true;\r
- ullNum = (uint64_t)-llNum;\r
- }\r
- else\r
- {\r
- fSign = false;\r
- ullNum = (uint64_t)llNum;\r
- }\r
-\r
- /* Not allowed to assume that 64-bit division is OK, so use a\r
- software division routine.\r
- */\r
- do\r
- {\r
- uint64_t ullQuotient;\r
- uint32_t ulRemainder;\r
-\r
- /* Note: RedUint64DivMod32() is smart enough to use normal division\r
- once ullNumericVal <= UINT32_MAX.\r
- */\r
- ullQuotient = RedUint64DivMod32(ullNum, 10U, &ulRemainder);\r
-\r
- ach[ulDigits] = gacDigits[ulRemainder];\r
- ullNum = ullQuotient;\r
- ulDigits++;\r
- }\r
- while(ullNum > 0U);\r
-\r
- if(fSign)\r
- {\r
- ach[ulDigits] = '-';\r
- ulDigits++;\r
- }\r
-\r
- ulLen = FinishToA(ach, ulDigits, pcBuffer, ulBufferLen, ulFillLen, cFill);\r
- }\r
-\r
- return ulLen;\r
-}\r
-\r
-\r
-/** @brief Format an unsigned 32-bit integer as an ASCII string as decimal or\r
- hex.\r
-\r
- @note If the output buffer length is exhausted, the result will *not* be\r
- null-terminated.\r
-\r
- @param pcBuffer The output buffer\r
- @param ulBufferLen The output buffer length\r
- @param ulNum The 32-bit unsigned number to convert\r
- @param fHex If true, format as hex; if false, decimal.\r
- @param ulFillLen The fill length, if any\r
- @param cFill The fill character to use\r
-\r
- @return The length of the string.\r
-*/\r
-static uint32_t ULtoA(\r
- char *pcBuffer,\r
- uint32_t ulBufferLen,\r
- uint32_t ulNum,\r
- bool fHex,\r
- uint32_t ulFillLen,\r
- char cFill)\r
-{\r
- uint32_t ulLen;\r
-\r
- if(pcBuffer == NULL)\r
- {\r
- REDERROR();\r
- ulLen = 0U;\r
- }\r
- else\r
- {\r
- char ach[11U]; /* Big enough for a uint32_t in radix 10 */\r
- uint32_t ulDigits = 0U;\r
- uint32_t ulNumericVal = ulNum;\r
- uint32_t ulRadix = fHex ? 16U : 10U;\r
-\r
- do\r
- {\r
- ach[ulDigits] = gacDigits[ulNumericVal % ulRadix];\r
- ulNumericVal = ulNumericVal / ulRadix;\r
- ulDigits++;\r
- }\r
- while(ulNumericVal > 0U);\r
-\r
- ulLen = FinishToA(ach, ulDigits, pcBuffer, ulBufferLen, ulFillLen, cFill);\r
- }\r
-\r
- return ulLen;\r
-}\r
-\r
-\r
-/** @brief Format an unsigned 64-bit integer as an ASCII string as decimal or\r
- hex.\r
-\r
- @note If the output buffer length is exhausted, the result will *not* be\r
- null-terminated.\r
-\r
- @param pcBuffer The output buffer.\r
- @param ulBufferLen The output buffer length.\r
- @param ullNum The unsigned 64-bit number to convert.\r
- @param fHex If true, format as hex; if false, decimal.\r
- @param ulFillLen The fill length, if any.\r
- @param cFill The fill character to use.\r
-\r
- @return The length of the string.\r
-*/\r
-static uint32_t ULLtoA(\r
- char *pcBuffer,\r
- uint32_t ulBufferLen,\r
- uint64_t ullNum,\r
- bool fHex,\r
- uint32_t ulFillLen,\r
- char cFill)\r
-{\r
- uint32_t ulLen;\r
-\r
- if(pcBuffer == NULL)\r
- {\r
- REDERROR();\r
- ulLen = 0U;\r
- }\r
- else\r
- {\r
-\r
- char ach[21U]; /* Big enough for a uint64_t in radix 10 */\r
- uint32_t ulDigits = 0U;\r
- uint64_t ullNumericVal = ullNum;\r
-\r
- if(fHex)\r
- {\r
- /* We can figure out the digits using bit operations.\r
- */\r
- do\r
- {\r
- ach[ulDigits] = gacDigits[ullNumericVal & 15U];\r
- ullNumericVal >>= 4U;\r
- ulDigits++;\r
- }\r
- while(ullNumericVal > 0U);\r
- }\r
- else\r
- {\r
- /* Not allowed to assume that 64-bit division is OK, so use a\r
- software division routine.\r
- */\r
- do\r
- {\r
- uint64_t ullQuotient;\r
- uint32_t ulRemainder;\r
-\r
- /* Note: RedUint64DivMod32() is smart enough to use normal division\r
- once ullNumericVal <= UINT32_MAX.\r
- */\r
- ullQuotient = RedUint64DivMod32(ullNumericVal, 10U, &ulRemainder);\r
-\r
- ach[ulDigits] = gacDigits[ulRemainder];\r
- ullNumericVal = ullQuotient;\r
- ulDigits++;\r
- }\r
- while(ullNumericVal > 0U);\r
- }\r
-\r
- ulLen = FinishToA(ach, ulDigits, pcBuffer, ulBufferLen, ulFillLen, cFill);\r
- }\r
-\r
- return ulLen;\r
-}\r
-\r
-\r
-/** @brief Finish converting a number into an ASCII string representing that\r
- number.\r
-\r
- This helper function contains common logic that needs to run at the end of\r
- all the "toA" functions. It adds the fill character and reverses the digits\r
- string.\r
-\r
- @param pcDigits The digits (and sign) for the ASCII string, in reverse\r
- order as they were computed.\r
- @param ulDigits The number of digit characters.\r
- @param pcOutBuffer The output buffer.\r
- @param ulBufferLen The length of the output buffer.\r
- @param ulFillLen The fill length. If the number string is shorter than\r
- this, the remaining bytes are filled with @p cFill.\r
- @param cFill The fill character.\r
-\r
- @return The length of @p pcOutBuffer.\r
-*/\r
-static uint32_t FinishToA(\r
- const char *pcDigits,\r
- uint32_t ulDigits,\r
- char *pcOutBuffer,\r
- uint32_t ulBufferLen,\r
- uint32_t ulFillLen,\r
- char cFill)\r
-{\r
- uint32_t ulIdx = 0U;\r
- uint32_t ulDigitIdx = ulDigits;\r
-\r
- /* user may have asked for a fill char\r
- */\r
- if(ulFillLen > ulDigits)\r
- {\r
- uint32_t ulFillRem = ulFillLen - ulDigits;\r
-\r
- while((ulFillRem > 0U) && (ulIdx < ulBufferLen))\r
- {\r
- pcOutBuffer[ulIdx] = cFill;\r
- ulIdx++;\r
- ulFillRem--;\r
- }\r
- }\r
-\r
- /* reverse the string\r
- */\r
- while((ulDigitIdx > 0) && (ulIdx < ulBufferLen))\r
- {\r
- ulDigitIdx--;\r
- pcOutBuffer[ulIdx] = pcDigits[ulDigitIdx];\r
- ulIdx++;\r
- }\r
-\r
- if(ulIdx < ulBufferLen)\r
- {\r
- pcOutBuffer[ulIdx] = '\0';\r
- }\r
-\r
- return ulIdx;\r
-}\r
-\r
+/* ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
+
+ Copyright (c) 2014-2015 Datalight, Inc.
+ All Rights Reserved Worldwide.
+
+ 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; use version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but "AS-IS," 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+/* Businesses and individuals that for commercial or other reasons cannot
+ comply with the terms of the GPLv2 license may obtain a commercial license
+ before incorporating Reliance Edge into proprietary software for
+ distribution in any form. Visit http://www.datalight.com/reliance-edge for
+ more information.
+*/
+/** @file
+ @brief Implements functions for printing.
+
+ These functions are intended to be used in portable test code, which cannot
+ assume the standard I/O functions will be available. Similar to their ANSI
+ C counterparts, these functions allow formatting text strings and (if the
+ configuration allows it) outputing formatted text. The latter ability
+ relies on the RedOsOutputString() OS service function.
+
+ Do *not* use these functions in code which can safely assume the standard
+ I/O functions are available (e.g., in host tools code).
+
+ Do *not* use these functions from within the file system driver. These
+ functions use variable arguments and thus are not MISRA-C:2012 compliant.
+*/
+#include <redfs.h>
+#include <redtestutils.h>
+#include <limits.h>
+#include <stdarg.h>
+
+
+/** @brief Maximum number of bytes of output supported by RedPrintf().
+
+ Typically only Datalight code uses these functions, and that could should be
+ written to respect this limit, so it should not normally be necessary to
+ adjust this value.
+*/
+#define OUTPUT_BUFFER_SIZE 256U
+
+
+typedef enum
+{
+ PRFMT_UNKNOWN = 0,
+ PRFMT_CHAR,
+ PRFMT_ANSISTRING,
+ PRFMT_SIGNED8BIT,
+ PRFMT_UNSIGNED8BIT,
+ PRFMT_SIGNED16BIT,
+ PRFMT_UNSIGNED16BIT,
+ PRFMT_SIGNED32BIT,
+ PRFMT_UNSIGNED32BIT,
+ PRFMT_SIGNED64BIT,
+ PRFMT_UNSIGNED64BIT,
+ PRFMT_HEX8BIT,
+ PRFMT_HEX16BIT,
+ PRFMT_HEX32BIT,
+ PRFMT_HEX64BIT,
+ PRFMT_POINTER,
+ PRFMT_DOUBLEPERCENT
+} PRINTTYPE;
+
+typedef struct
+{
+ PRINTTYPE type; /* The PRFMT_* type found */
+ uint32_t ulSpecifierIdx; /* Returns a pointer to the % sign */
+ uint32_t ulFillLen;
+ char cFillChar;
+ bool fLeftJustified;
+ bool fHasIllegalType; /* TRUE if an illegal sequence was skipped over */
+ bool fHasVarWidth;
+} PRINTFORMAT;
+
+
+/* Our output handlers are written for standard fixed width data types. Map
+ the standard ANSI C data types onto our handlers. Currently this code has
+ the following requirements:
+
+ 1) shorts must be either 16 or 32 bits
+ 2) ints must be either 16 or 32 bits
+ 3) longs must be between 32 or 64 bits
+ 4) long longs must be 64 bits
+*/
+#if (USHRT_MAX == 0xFFFFU)
+ #define MAPSHORT PRFMT_SIGNED16BIT
+ #define MAPUSHORT PRFMT_UNSIGNED16BIT
+ #define MAPHEXUSHORT PRFMT_HEX16BIT
+#elif (USHRT_MAX == 0xFFFFFFFFU)
+ #define MAPSHORT PRFMT_SIGNED32BIT
+ #define MAPUSHORT PRFMT_UNSIGNED32BIT
+ #define MAPHEXUSHORT PRFMT_HEX32BIT
+#else
+ #error "The 'short' data type does not have a 16 or 32-bit width"
+#endif
+
+#if (UINT_MAX == 0xFFFFU)
+ #define MAPINT PRFMT_SIGNED16BIT
+ #define MAPUINT PRFMT_UNSIGNED16BIT
+ #define MAPHEXUINT PRFMT_HEX16BIT
+#elif (UINT_MAX == 0xFFFFFFFFU)
+ #define MAPINT PRFMT_SIGNED32BIT
+ #define MAPUINT PRFMT_UNSIGNED32BIT
+ #define MAPHEXUINT PRFMT_HEX32BIT
+#else
+ #error "The 'int' data type does not have a 16 or 32-bit width"
+#endif
+
+#if (ULONG_MAX == 0xFFFFFFFFU)
+ #define MAPLONG PRFMT_SIGNED32BIT
+ #define MAPULONG PRFMT_UNSIGNED32BIT
+ #define MAPHEXULONG PRFMT_HEX32BIT
+#elif (ULONG_MAX <= UINT64_SUFFIX(0xFFFFFFFFFFFFFFFF))
+ /* We've run into unusual environments where "longs" are 40-bits wide.
+ In this event, map them to 64-bit types so no data is lost.
+ */
+ #define MAPLONG PRFMT_SIGNED64BIT
+ #define MAPULONG PRFMT_UNSIGNED64BIT
+ #define MAPHEXULONG PRFMT_HEX64BIT
+#else
+ #error "The 'long' data type is not between 32 and 64 bits wide"
+#endif
+
+#if defined(ULLONG_MAX) && (ULLONG_MAX != UINT64_SUFFIX(0xFFFFFFFFFFFFFFFF))
+ #error "The 'long long' data type is not 64 bits wide"
+#else
+ #define MAPLONGLONG PRFMT_SIGNED64BIT
+ #define MAPULONGLONG PRFMT_UNSIGNED64BIT
+ #define MAPHEXULONGLONG PRFMT_HEX64BIT
+#endif
+
+
+static uint32_t ProcessFormatSegment(char *pcBuffer, uint32_t ulBufferLen, const char *pszFormat, PRINTFORMAT *pFormat, uint32_t *pulSpecifierLen);
+static uint32_t ParseFormatSpecifier(char const *pszFormat, PRINTFORMAT *pFormatType);
+static PRINTTYPE ParseFormatType(const char *pszFormat, uint32_t *pulTypeLen);
+static uint32_t LtoA(char *pcBuffer, uint32_t ulBufferLen, int32_t lNum, uint32_t ulFillLen, char cFill);
+static uint32_t LLtoA(char *pcBuffer, uint32_t ulBufferLen, int64_t llNum, uint32_t ulFillLen, char cFill);
+static uint32_t ULtoA(char *pcBuffer, uint32_t ulBufferLen, uint32_t ulNum, bool fHex, uint32_t ulFillLen, char cFill);
+static uint32_t ULLtoA(char *pcBuffer, uint32_t ulBufferLen, uint64_t ullNum, bool fHex, uint32_t ulFillLen, char cFill);
+static uint32_t FinishToA(const char *pcDigits, uint32_t ulDigits, char *pcOutBuffer, uint32_t ulBufferLen, uint32_t ulFillLen, char cFill);
+
+
+/* Digits for the *LtoA() routines.
+*/
+static const char gacDigits[] = "0123456789ABCDEF";
+
+
+#if REDCONF_OUTPUT == 1
+/** @brief Print formatted data with a variable length argument list.
+
+ This function provides a subset of the ANSI C printf() functionality with
+ several extensions to support fixed size data types.
+
+ See RedVSNPrintf() for the list of supported types.
+
+ @param pszFormat A pointer to the null-terminated format string.
+ @param ... The variable length argument list.
+*/
+void RedPrintf(
+ const char *pszFormat,
+ ...)
+{
+ va_list arglist;
+
+ va_start(arglist, pszFormat);
+
+ RedVPrintf(pszFormat, arglist);
+
+ va_end(arglist);
+}
+
+
+/** @brief Print formatted data using a pointer to a variable length argument
+ list.
+
+ This function provides a subset of the ANSI C vprintf() functionality.
+
+ See RedVSNPrintf() for the list of supported types.
+
+ This function accommodates a maximum output length of #OUTPUT_BUFFER_SIZE.
+ If this function must truncate the output, and the original string was
+ \n terminated, the truncated output will be \n terminated as well.
+
+ @param pszFormat A pointer to the null-terminated format string.
+ @param arglist The variable length argument list.
+*/
+void RedVPrintf(
+ const char *pszFormat,
+ va_list arglist)
+{
+ char achBuffer[OUTPUT_BUFFER_SIZE];
+
+ if(RedVSNPrintf(achBuffer, sizeof(achBuffer), pszFormat, arglist) == -1)
+ {
+ /* Ensture the buffer is null terminated.
+ */
+ achBuffer[sizeof(achBuffer) - 1U] = '\0';
+
+ /* If the original string was \n terminated and the new one is not, due to
+ truncation, stuff a \n into the new one.
+ */
+ if(pszFormat[RedStrLen(pszFormat) - 1U] == '\n')
+ {
+ achBuffer[sizeof(achBuffer) - 2U] = '\n';
+ }
+ }
+
+ RedOsOutputString(achBuffer);
+}
+#endif /* #if REDCONF_OUTPUT == 1 */
+
+
+/** @brief Format arguments into a string using a subset of the ANSI C
+ vsprintf() functionality.
+
+ This function is modeled after the Microsoft _snprint() extension to the
+ ANSI C sprintf() function, and allows a buffer length to be specified so
+ that overflow is avoided.
+
+ See RedVSNPrintf() for the list of supported types.
+
+ @param pcBuffer A pointer to the output buffer
+ @param ulBufferLen The output buffer length
+ @param pszFormat A pointer to the null terminated format string
+ @param ... Variable argument list
+
+ @return The length output, or -1 if the buffer filled up. If -1 is
+ returned, the output buffer may not be null-terminated.
+*/
+int32_t RedSNPrintf(
+ char *pcBuffer,
+ uint32_t ulBufferLen,
+ const char *pszFormat,
+ ...)
+{
+ int32_t iLen;
+ va_list arglist;
+
+ va_start(arglist, pszFormat);
+
+ iLen = RedVSNPrintf(pcBuffer, ulBufferLen, pszFormat, arglist);
+
+ va_end(arglist);
+
+ return iLen;
+}
+
+
+/** @brief Format arguments into a string using a subset of the ANSI C
+ vsprintf() functionality.
+
+ This function is modeled after the Microsoft _vsnprint() extension to the
+ ANSI C vsprintf() function, and requires a buffer length to be specified so
+ that overflow is avoided.
+
+ The following ANSI C standard formatting codes are supported:
+
+ | Code | Meaning |
+ | ---- | ---------------------------------- |
+ | %c | Format a character |
+ | %s | Format a null-terminated C string |
+ | %hd | Format a signed short |
+ | %hu | Format an unsigned short |
+ | %d | Format a signed integer |
+ | %u | Format an unsigned integer |
+ | %ld | Format a signed long |
+ | %lu | Format an unsigned long |
+ | %lld | Format a signed long long |
+ | %llu | Format an unsigned long long |
+ | %hx | Format a short in hex |
+ | %x | Format an integer in hex |
+ | %lx | Format a long in hex |
+ | %llx | Format a long long in hex |
+ | %p | Format a pointer (hex value) |
+
+ @note All formatting codes are case-sensitive.
+
+ Fill characters and field widths are supported per the ANSI standard, as is
+ left justification with the '-' character.
+
+ The only supported fill characters are '0', ' ', and '_'.
+
+ '*' is supported to specify variable length field widths.
+
+ Hexidecimal numbers are always displayed in upper case. Formatting codes
+ which specifically request upper case (e.g., "%lX") are not supported.
+
+ Unsupported behaviors:
+ - Precision is not supported.
+ - Floating point is not supported.
+
+ Errata:
+ - There is a subtle difference in the return value for this function versus
+ the Microsoft implementation. In the Microsoft version, if the buffer
+ exactly fills up, but there is no room for a null-terminator, the return
+ value will be the length of the buffer. In this code, -1 will be returned
+ when this happens.
+ - When using left justified strings, the only supported fill character is a
+ space, regardless of what may be specified. It is not clear if this is
+ ANSI standard or just the way the Microsoft function works, but we emulate
+ the Microsoft behavior.
+
+ @param pcBuffer A pointer to the output buffer.
+ @param ulBufferLen The output buffer length.
+ @param pszFormat A pointer to the null terminated ANSI format string.
+ @param arglist Variable argument list.
+
+ @return The length output, or -1 if the buffer filled up. If -1 is
+ returned, the output buffer may not be null-terminated.
+*/
+int32_t RedVSNPrintf(
+ char *pcBuffer,
+ uint32_t ulBufferLen,
+ const char *pszFormat,
+ va_list arglist)
+{
+ uint32_t ulBufIdx = 0U;
+ uint32_t ulFmtIdx = 0U;
+ int32_t iLen;
+
+ while((pszFormat[ulFmtIdx] != '\0') && (ulBufIdx < ulBufferLen))
+ {
+ PRINTFORMAT fmt;
+ uint32_t ulSpecifierLen;
+ uint32_t ulWidth;
+
+ /* Process the next segment of the format string, outputting
+ any non-format specifiers, as output buffer space allows,
+ and return information about the next format specifier.
+ */
+ ulWidth = ProcessFormatSegment(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, &pszFormat[ulFmtIdx], &fmt, &ulSpecifierLen);
+ if(ulWidth)
+ {
+ REDASSERT(ulWidth <= (ulBufferLen - ulBufIdx));
+
+ ulBufIdx += ulWidth;
+ }
+
+ /* If no specifier was found, or if the output buffer is
+ full, we're done -- get out.
+ */
+ if((ulSpecifierLen == 0U) || (ulBufIdx == ulBufferLen))
+ {
+ break;
+ }
+
+ /* Otherwise, the math should add up for these things...
+ */
+ REDASSERT(&pszFormat[fmt.ulSpecifierIdx] == &pszFormat[ulWidth]);
+
+ /* Point past the specifier, to the next piece of the format string.
+ */
+ ulFmtIdx = ulFmtIdx + fmt.ulSpecifierIdx + ulSpecifierLen;
+
+ if(fmt.fHasVarWidth)
+ {
+ int iFillLen = va_arg(arglist, int);
+
+ if(iFillLen >= 0)
+ {
+ fmt.ulFillLen = (uint32_t)iFillLen;
+ }
+ else
+ {
+ /* Bogus fill length. Ignore.
+ */
+ fmt.ulFillLen = 0U;
+ }
+ }
+
+ switch(fmt.type)
+ {
+ case PRFMT_DOUBLEPERCENT:
+ {
+ /* Nothing to do. A single percent has already been output,
+ and we just finished skipping past the second percent.
+ */
+ break;
+ }
+
+ /*-----------------> Small int handling <------------------
+ *
+ * Values smaller than "int" will be promoted to "int" by
+ * the compiler, so we must retrieve them using "int" when
+ * calling va_arg(). Once we've done that, we immediately
+ * put the value into the desired data type.
+ *---------------------------------------------------------*/
+
+ case PRFMT_CHAR:
+ {
+ pcBuffer[ulBufIdx] = (char)va_arg(arglist, int);
+ ulBufIdx++;
+ break;
+ }
+ case PRFMT_SIGNED8BIT:
+ {
+ int8_t num = (int8_t)va_arg(arglist, int);
+
+ ulBufIdx += LtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, num, fmt.ulFillLen, fmt.cFillChar);
+ break;
+ }
+ case PRFMT_UNSIGNED8BIT:
+ {
+ uint8_t bNum = (uint8_t)va_arg(arglist, unsigned);
+
+ ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, bNum, false, fmt.ulFillLen, fmt.cFillChar);
+ break;
+ }
+ case PRFMT_HEX8BIT:
+ {
+ uint8_t bNum = (uint8_t)va_arg(arglist, unsigned);
+
+ ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, bNum, true, fmt.ulFillLen, fmt.cFillChar);
+ break;
+ }
+ case PRFMT_SIGNED16BIT:
+ {
+ int16_t num = (int16_t)va_arg(arglist, int);
+
+ ulBufIdx += LtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, num, fmt.ulFillLen, fmt.cFillChar);
+ break;
+ }
+ case PRFMT_UNSIGNED16BIT:
+ {
+ uint16_t uNum = (uint16_t)va_arg(arglist, unsigned);
+
+ ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, uNum, false, fmt.ulFillLen, fmt.cFillChar);
+ break;
+ }
+ case PRFMT_HEX16BIT:
+ {
+ uint16_t uNum = (uint16_t)va_arg(arglist, unsigned);
+
+ ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, uNum, true, fmt.ulFillLen, fmt.cFillChar);
+ break;
+ }
+ case PRFMT_SIGNED32BIT:
+ {
+ int32_t lNum = va_arg(arglist, int32_t);
+
+ ulBufIdx += LtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, lNum, fmt.ulFillLen, fmt.cFillChar);
+ break;
+ }
+ case PRFMT_UNSIGNED32BIT:
+ {
+ uint32_t ulNum = va_arg(arglist, uint32_t);
+
+ ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, ulNum, false, fmt.ulFillLen, fmt.cFillChar);
+ break;
+ }
+ case PRFMT_HEX32BIT:
+ {
+ uint32_t ulNum = va_arg(arglist, uint32_t);
+
+ ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, ulNum, true, fmt.ulFillLen, fmt.cFillChar);
+ break;
+ }
+ case PRFMT_SIGNED64BIT:
+ {
+ int64_t llNum = va_arg(arglist, int64_t);
+
+ ulBufIdx += LLtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, llNum, fmt.ulFillLen, fmt.cFillChar);
+ break;
+ }
+ case PRFMT_UNSIGNED64BIT:
+ {
+ uint64_t ullNum = va_arg(arglist, uint64_t);
+
+ ulBufIdx += ULLtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, ullNum, false, fmt.ulFillLen, fmt.cFillChar);
+ break;
+ }
+ case PRFMT_HEX64BIT:
+ {
+ uint64_t ullNum = va_arg(arglist, uint64_t);
+
+ ulBufIdx += ULLtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, ullNum, true, fmt.ulFillLen, fmt.cFillChar);
+ break;
+ }
+ case PRFMT_POINTER:
+ {
+ const void *ptr = va_arg(arglist, const void *);
+
+ /* Assert our assumption.
+ */
+ REDASSERT(sizeof(void *) <= 8U);
+
+ /* Format as either a 64-bit or a 32-bit value.
+ */
+ if(sizeof(void *) > 4U)
+ {
+ /* Attempt to quiet warnings.
+ */
+ uintptr_t ptrval = (uintptr_t)ptr;
+ uint64_t ullPtrVal = (uint64_t)ptrval;
+
+ ulBufIdx += ULLtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, ullPtrVal, true, fmt.ulFillLen, fmt.cFillChar);
+ }
+ else
+ {
+ /* Attempt to quiet warnings.
+ */
+ uintptr_t ptrval = (uintptr_t)ptr;
+ uint32_t ulPtrVal = (uint32_t)ptrval;
+
+ ulBufIdx += ULtoA(&pcBuffer[ulBufIdx], ulBufferLen - ulBufIdx, ulPtrVal, true, fmt.ulFillLen, fmt.cFillChar);
+ }
+
+ break;
+ }
+ case PRFMT_ANSISTRING:
+ {
+ const char *pszArg = va_arg(arglist, const char *);
+ uint32_t ulArgIdx = 0U;
+
+ if(pszArg == NULL)
+ {
+ pszArg = "null";
+ }
+
+ if(fmt.ulFillLen > 0U)
+ {
+ if(!fmt.fLeftJustified)
+ {
+ uint32_t ulLen = RedStrLen(pszArg);
+
+ /* So long as we are not left justifying, fill as many
+ characters as is necessary to make the string right
+ justified.
+ */
+ while(((ulBufferLen - ulBufIdx) > 0U) && (fmt.ulFillLen > ulLen))
+ {
+ pcBuffer[ulBufIdx] = fmt.cFillChar;
+ ulBufIdx++;
+ fmt.ulFillLen--;
+ }
+ }
+
+ /* Move as many characters as we have space for into the
+ output buffer.
+ */
+ while(((ulBufferLen - ulBufIdx) > 0U) && (pszArg[ulArgIdx] != '\0'))
+ {
+ pcBuffer[ulBufIdx] = pszArg[ulArgIdx];
+ ulBufIdx++;
+ ulArgIdx++;
+ if(fmt.ulFillLen > 0U)
+ {
+ fmt.ulFillLen--;
+ }
+ }
+
+ /* If there is any space left to fill, do it (the string
+ must have been left justified).
+ */
+ while(((ulBufferLen - ulBufIdx) > 0U) && (fmt.ulFillLen > 0U))
+ {
+ /* This is NOT a typo -- when using left justified
+ strings, spaces are the only allowed fill character.
+ See the errata.
+ */
+ pcBuffer[ulBufIdx] = ' ';
+ ulBufIdx++;
+ fmt.ulFillLen--;
+ }
+ }
+ else
+ {
+ /* No fill characters, just move up to as many
+ characters as we have space for in the output
+ buffer.
+ */
+ while(((ulBufferLen - ulBufIdx) > 0U) && (pszArg[ulArgIdx] != '\0'))
+ {
+ pcBuffer[ulBufIdx] = pszArg[ulArgIdx];
+ ulBufIdx++;
+ ulArgIdx++;
+ }
+ }
+ break;
+ }
+ default:
+ {
+ REDERROR();
+ break;
+ }
+ }
+ }
+
+ /* If there is space, tack on a null and return the output length
+ processed, not including the null.
+ */
+ if(ulBufIdx < ulBufferLen)
+ {
+ pcBuffer[ulBufIdx] = '\0';
+ iLen = (int32_t)ulBufIdx;
+ }
+ else
+ {
+ /* Not enough space, just return -1, with no null termination
+ */
+ iLen = -1;
+ }
+
+ return iLen;
+}
+
+
+/** @brief Process the next segment of the format string, outputting any
+ non-format specifiers, as output buffer space allows, and return
+ information about the next format specifier.
+
+ @note If the returned value is the same as the supplied @p ulBufferLen,
+ the output buffer will not be null-terminated. In all other cases,
+ the result will be null-terminated. The returned length will never
+ include the null in the count.
+
+ @param pcBuffer The output buffer.
+ @param ulBufferLen The output buffer length.
+ @param pszFormat The format string to process.
+ @param pFormat The PRINTFORMAT structure to fill.
+ @param pulSpecifierLen Returns the length of any format specifier string,
+ or zero if no specifier was found.
+
+ @return The count of characters from pszFormatt which were processed and
+ copied to pcBuffer.
+ - If zero is returned and *pulSpecifierLen is non-zero, then
+ a format specifier string was found at the start of pszFmt.
+ - If non-zero is returned and *pulSpecifierLen is zero, then
+ no format specifier string was found, and the entire pszFmt
+ string was copied to pBuffer (or as much as will fit).
+*/
+static uint32_t ProcessFormatSegment(
+ char *pcBuffer,
+ uint32_t ulBufferLen,
+ const char *pszFormat,
+ PRINTFORMAT *pFormat,
+ uint32_t *pulSpecifierLen)
+{
+ uint32_t ulWidth = 0U;
+
+ /* Find the next format specifier string, and information about it.
+ */
+ *pulSpecifierLen = ParseFormatSpecifier(pszFormat, pFormat);
+
+ if(*pulSpecifierLen == 0U)
+ {
+ /* If no specifier was found at all, then simply output the full length
+ of the string, or as much as will fit.
+ */
+ ulWidth = REDMIN(ulBufferLen, RedStrLen(pszFormat));
+
+ RedMemCpy(pcBuffer, pszFormat, ulWidth);
+ }
+ else
+ {
+ /* If we encountered a double percent, skip past one of them so it is
+ copied into the output buffer.
+ */
+ if(pFormat->type == PRFMT_DOUBLEPERCENT)
+ {
+ pFormat->ulSpecifierIdx++;
+
+ /* A double percent specifier always has a length of two. Since
+ we're processing one of those percent signs, reduce the length
+ to one. Assert it so.
+ */
+ REDASSERT(*pulSpecifierLen == 2U);
+
+ (*pulSpecifierLen)--;
+ }
+
+ /* So long as the specifier is not the very first thing in the format
+ string...
+ */
+ if(pFormat->ulSpecifierIdx != 0U)
+ {
+ /* A specifier was found, but there is other data preceding it.
+ Copy as much as allowed to the output buffer.
+ */
+ ulWidth = REDMIN(ulBufferLen, pFormat->ulSpecifierIdx);
+
+ RedMemCpy(pcBuffer, pszFormat, ulWidth);
+ }
+ }
+
+ /* If there is room in the output buffer, null-terminate whatever is there.
+ But note that the returned length never includes the null.
+ */
+ if(ulWidth < ulBufferLen)
+ {
+ pcBuffer[ulWidth] = 0U;
+ }
+
+ return ulWidth;
+}
+
+
+/** @brief Parse the specified format string for a valid RedVSNPrintf() format
+ sequence, and return information about it.
+
+ @param pszFormat The format string to process.
+ @param pFormatType The PRINTFORMAT structure to fill. The data is only
+ valid if a non-zero length is returned.
+
+ @return The length of the full format specifier string, starting at
+ pFormat->ulSpecifierIdx. Returns zero if a valid specifier was
+ not found.
+*/
+static uint32_t ParseFormatSpecifier(
+ char const *pszFormat,
+ PRINTFORMAT *pFormatType)
+{
+ bool fContainsIllegalSequence = false;
+ uint32_t ulLen = 0U;
+ uint32_t ulIdx = 0U;
+
+ while(pszFormat[ulIdx] != '\0')
+ {
+ uint32_t ulTypeLen;
+
+ /* general output
+ */
+ if(pszFormat[ulIdx] != '%')
+ {
+ ulIdx++;
+ }
+ else
+ {
+ RedMemSet(pFormatType, 0U, sizeof(*pFormatType));
+
+ /* Record the location of the start of the format sequence
+ */
+ pFormatType->ulSpecifierIdx = ulIdx;
+ ulIdx++;
+
+ if(pszFormat[ulIdx] == '-')
+ {
+ pFormatType->fLeftJustified = true;
+ ulIdx++;
+ }
+
+ if((pszFormat[ulIdx] == '0') || (pszFormat[ulIdx] == '_'))
+ {
+ pFormatType->cFillChar = pszFormat[ulIdx];
+ ulIdx++;
+ }
+ else
+ {
+ pFormatType->cFillChar = ' ';
+ }
+
+ if(pszFormat[ulIdx] == '*')
+ {
+ pFormatType->fHasVarWidth = true;
+ ulIdx++;
+ }
+ else if(ISDIGIT(pszFormat[ulIdx]))
+ {
+ pFormatType->ulFillLen = (uint32_t)RedAtoI(&pszFormat[ulIdx]);
+ while(ISDIGIT(pszFormat[ulIdx]))
+ {
+ ulIdx++;
+ }
+ }
+ else
+ {
+ /* No fill length.
+ */
+ }
+
+ pFormatType->type = ParseFormatType(&pszFormat[ulIdx], &ulTypeLen);
+ if(pFormatType->type != PRFMT_UNKNOWN)
+ {
+ /* Even though we are returning successfully, keep track of
+ whether an illegal sequence was encountered and skipped.
+ */
+ pFormatType->fHasIllegalType = fContainsIllegalSequence;
+
+ ulLen = (ulIdx - pFormatType->ulSpecifierIdx) + ulTypeLen;
+ break;
+ }
+
+ /* In the case of an unrecognized type string, simply ignore
+ it entirely. Reset the pointer to the position following
+ the percent sign, so it is not found again.
+ */
+ fContainsIllegalSequence = false;
+ ulIdx = pFormatType->ulSpecifierIdx + 1U;
+ }
+ }
+
+ return ulLen;
+}
+
+
+/** @brief Parse a RedPrintf() format type string to determine the proper data
+ type.
+
+ @param pszFormat The format string to process. This must be a pointer to
+ the character following any width or justification
+ characters.
+ @param pulTypeLen The location in which to store the type length. The
+ value will be 0 if PRFMT_UNKNOWN is returned.
+
+ @return Rhe PRFMT_* type value, or PRFMT_UNKNOWN if the type is not
+ recognized.
+*/
+static PRINTTYPE ParseFormatType(
+ const char *pszFormat,
+ uint32_t *pulTypeLen)
+{
+ PRINTTYPE fmtType = PRFMT_UNKNOWN;
+ uint32_t ulIdx = 0U;
+
+ switch(pszFormat[ulIdx])
+ {
+ case '%':
+ fmtType = PRFMT_DOUBLEPERCENT;
+ break;
+ case 'c':
+ fmtType = PRFMT_CHAR;
+ break;
+ case 's':
+ fmtType = PRFMT_ANSISTRING;
+ break;
+ case 'p':
+ fmtType = PRFMT_POINTER;
+ break;
+ case 'd':
+ fmtType = MAPINT;
+ break;
+ case 'u':
+ fmtType = MAPUINT;
+ break;
+ case 'x':
+ fmtType = MAPHEXUINT;
+ break;
+ case 'h':
+ {
+ ulIdx++;
+ switch(pszFormat[ulIdx])
+ {
+ case 'd':
+ fmtType = MAPSHORT;
+ break;
+ case 'u':
+ fmtType = MAPUSHORT;
+ break;
+ case 'x':
+ fmtType = MAPHEXUSHORT;
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case 'l':
+ {
+ ulIdx++;
+ switch(pszFormat[ulIdx])
+ {
+ case 'd':
+ fmtType = MAPLONG;
+ break;
+ case 'u':
+ fmtType = MAPULONG;
+ break;
+ case 'x':
+ fmtType = MAPHEXULONG;
+ break;
+ case 'l':
+ {
+ ulIdx++;
+ switch(pszFormat[ulIdx])
+ {
+ case 'd':
+ fmtType = MAPLONGLONG;
+ break;
+ case 'u':
+ fmtType = MAPULONGLONG;
+ break;
+ case 'x':
+ case 'X':
+ fmtType = MAPHEXULONGLONG;
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ if(fmtType != PRFMT_UNKNOWN)
+ {
+ *pulTypeLen = ulIdx + 1U;
+ }
+ else
+ {
+ *pulTypeLen = 0U;
+ }
+
+ return fmtType;
+}
+
+
+/** @brief Format a signed 32-bit integer as a base 10 ASCII string.
+
+ @note If the output buffer length is exhausted, the result will *not* be
+ null-terminated.
+
+ @note If the @p ulFillLen value is greater than or equal to the buffer
+ length, the result will not be null-terminated, even if the
+ formatted portion of the data is shorter than the buffer length.
+
+ @param pcBuffer The output buffer
+ @param ulBufferLen A pointer to the output buffer length
+ @param lNum The 32-bit signed number to convert
+ @param ulFillLen The fill length, if any
+ @param cFill The fill character to use
+
+ @return The length of the string.
+*/
+static uint32_t LtoA(
+ char *pcBuffer,
+ uint32_t ulBufferLen,
+ int32_t lNum,
+ uint32_t ulFillLen,
+ char cFill)
+{
+ uint32_t ulLen;
+
+ if(pcBuffer == NULL)
+ {
+ REDERROR();
+ ulLen = 0U;
+ }
+ else
+ {
+ char ach[12U]; /* big enough for a int32_t in base 10 */
+ uint32_t ulDigits = 0U;
+ uint32_t ulNum;
+ bool fSign;
+
+ if(lNum < 0)
+ {
+ fSign = true;
+ ulNum = (uint32_t)-lNum;
+ }
+ else
+ {
+ fSign = false;
+ ulNum = (uint32_t)lNum;
+ }
+
+ do
+ {
+ ach[ulDigits] = gacDigits[ulNum % 10U];
+ ulNum = ulNum / 10U;
+ ulDigits++;
+ }
+ while(ulNum);
+
+ if(fSign)
+ {
+ ach[ulDigits] = '-';
+ ulDigits++;
+ }
+
+ ulLen = FinishToA(ach, ulDigits, pcBuffer, ulBufferLen, ulFillLen, cFill);
+ }
+
+ return ulLen;
+}
+
+
+/** @brief Format a signed 64-bit integer as a base 10 ASCII string.
+
+ @note If the output buffer length is exhausted, the result will *not* be
+ null-terminated.
+
+ @note If the @p ulFillLen value is greater than or equal to the buffer
+ length, the result will not be null-terminated, even if the
+ formatted portion of the data is shorter than the buffer length.
+
+ @param pcBuffer The output buffer
+ @param ulBufferLen A pointer to the output buffer length
+ @param llNum The 64-bit signed number to convert
+ @param ulFillLen The fill length, if any
+ @param cFill The fill character to use
+
+ @return The length of the string.
+*/
+static uint32_t LLtoA(
+ char *pcBuffer,
+ uint32_t ulBufferLen,
+ int64_t llNum,
+ uint32_t ulFillLen,
+ char cFill)
+{
+ uint32_t ulLen;
+
+ if(pcBuffer == NULL)
+ {
+ REDERROR();
+ ulLen = 0U;
+ }
+ else
+ {
+ char ach[12U]; /* big enough for a int32_t in base 10 */
+ uint32_t ulDigits = 0U;
+ uint64_t ullNum;
+ bool fSign;
+
+ if(llNum < 0)
+ {
+ fSign = true;
+ ullNum = (uint64_t)-llNum;
+ }
+ else
+ {
+ fSign = false;
+ ullNum = (uint64_t)llNum;
+ }
+
+ /* Not allowed to assume that 64-bit division is OK, so use a
+ software division routine.
+ */
+ do
+ {
+ uint64_t ullQuotient;
+ uint32_t ulRemainder;
+
+ /* Note: RedUint64DivMod32() is smart enough to use normal division
+ once ullNumericVal <= UINT32_MAX.
+ */
+ ullQuotient = RedUint64DivMod32(ullNum, 10U, &ulRemainder);
+
+ ach[ulDigits] = gacDigits[ulRemainder];
+ ullNum = ullQuotient;
+ ulDigits++;
+ }
+ while(ullNum > 0U);
+
+ if(fSign)
+ {
+ ach[ulDigits] = '-';
+ ulDigits++;
+ }
+
+ ulLen = FinishToA(ach, ulDigits, pcBuffer, ulBufferLen, ulFillLen, cFill);
+ }
+
+ return ulLen;
+}
+
+
+/** @brief Format an unsigned 32-bit integer as an ASCII string as decimal or
+ hex.
+
+ @note If the output buffer length is exhausted, the result will *not* be
+ null-terminated.
+
+ @param pcBuffer The output buffer
+ @param ulBufferLen The output buffer length
+ @param ulNum The 32-bit unsigned number to convert
+ @param fHex If true, format as hex; if false, decimal.
+ @param ulFillLen The fill length, if any
+ @param cFill The fill character to use
+
+ @return The length of the string.
+*/
+static uint32_t ULtoA(
+ char *pcBuffer,
+ uint32_t ulBufferLen,
+ uint32_t ulNum,
+ bool fHex,
+ uint32_t ulFillLen,
+ char cFill)
+{
+ uint32_t ulLen;
+
+ if(pcBuffer == NULL)
+ {
+ REDERROR();
+ ulLen = 0U;
+ }
+ else
+ {
+ char ach[11U]; /* Big enough for a uint32_t in radix 10 */
+ uint32_t ulDigits = 0U;
+ uint32_t ulNumericVal = ulNum;
+ uint32_t ulRadix = fHex ? 16U : 10U;
+
+ do
+ {
+ ach[ulDigits] = gacDigits[ulNumericVal % ulRadix];
+ ulNumericVal = ulNumericVal / ulRadix;
+ ulDigits++;
+ }
+ while(ulNumericVal > 0U);
+
+ ulLen = FinishToA(ach, ulDigits, pcBuffer, ulBufferLen, ulFillLen, cFill);
+ }
+
+ return ulLen;
+}
+
+
+/** @brief Format an unsigned 64-bit integer as an ASCII string as decimal or
+ hex.
+
+ @note If the output buffer length is exhausted, the result will *not* be
+ null-terminated.
+
+ @param pcBuffer The output buffer.
+ @param ulBufferLen The output buffer length.
+ @param ullNum The unsigned 64-bit number to convert.
+ @param fHex If true, format as hex; if false, decimal.
+ @param ulFillLen The fill length, if any.
+ @param cFill The fill character to use.
+
+ @return The length of the string.
+*/
+static uint32_t ULLtoA(
+ char *pcBuffer,
+ uint32_t ulBufferLen,
+ uint64_t ullNum,
+ bool fHex,
+ uint32_t ulFillLen,
+ char cFill)
+{
+ uint32_t ulLen;
+
+ if(pcBuffer == NULL)
+ {
+ REDERROR();
+ ulLen = 0U;
+ }
+ else
+ {
+
+ char ach[21U]; /* Big enough for a uint64_t in radix 10 */
+ uint32_t ulDigits = 0U;
+ uint64_t ullNumericVal = ullNum;
+
+ if(fHex)
+ {
+ /* We can figure out the digits using bit operations.
+ */
+ do
+ {
+ ach[ulDigits] = gacDigits[ullNumericVal & 15U];
+ ullNumericVal >>= 4U;
+ ulDigits++;
+ }
+ while(ullNumericVal > 0U);
+ }
+ else
+ {
+ /* Not allowed to assume that 64-bit division is OK, so use a
+ software division routine.
+ */
+ do
+ {
+ uint64_t ullQuotient;
+ uint32_t ulRemainder;
+
+ /* Note: RedUint64DivMod32() is smart enough to use normal division
+ once ullNumericVal <= UINT32_MAX.
+ */
+ ullQuotient = RedUint64DivMod32(ullNumericVal, 10U, &ulRemainder);
+
+ ach[ulDigits] = gacDigits[ulRemainder];
+ ullNumericVal = ullQuotient;
+ ulDigits++;
+ }
+ while(ullNumericVal > 0U);
+ }
+
+ ulLen = FinishToA(ach, ulDigits, pcBuffer, ulBufferLen, ulFillLen, cFill);
+ }
+
+ return ulLen;
+}
+
+
+/** @brief Finish converting a number into an ASCII string representing that
+ number.
+
+ This helper function contains common logic that needs to run at the end of
+ all the "toA" functions. It adds the fill character and reverses the digits
+ string.
+
+ @param pcDigits The digits (and sign) for the ASCII string, in reverse
+ order as they were computed.
+ @param ulDigits The number of digit characters.
+ @param pcOutBuffer The output buffer.
+ @param ulBufferLen The length of the output buffer.
+ @param ulFillLen The fill length. If the number string is shorter than
+ this, the remaining bytes are filled with @p cFill.
+ @param cFill The fill character.
+
+ @return The length of @p pcOutBuffer.
+*/
+static uint32_t FinishToA(
+ const char *pcDigits,
+ uint32_t ulDigits,
+ char *pcOutBuffer,
+ uint32_t ulBufferLen,
+ uint32_t ulFillLen,
+ char cFill)
+{
+ uint32_t ulIdx = 0U;
+ uint32_t ulDigitIdx = ulDigits;
+
+ /* user may have asked for a fill char
+ */
+ if(ulFillLen > ulDigits)
+ {
+ uint32_t ulFillRem = ulFillLen - ulDigits;
+
+ while((ulFillRem > 0U) && (ulIdx < ulBufferLen))
+ {
+ pcOutBuffer[ulIdx] = cFill;
+ ulIdx++;
+ ulFillRem--;
+ }
+ }
+
+ /* reverse the string
+ */
+ while((ulDigitIdx > 0) && (ulIdx < ulBufferLen))
+ {
+ ulDigitIdx--;
+ pcOutBuffer[ulIdx] = pcDigits[ulDigitIdx];
+ ulIdx++;
+ }
+
+ if(ulIdx < ulBufferLen)
+ {
+ pcOutBuffer[ulIdx] = '\0';
+ }
+
+ return ulIdx;
+}
+
-/* ----> DO NOT REMOVE THE FOLLOWING NOTICE <----\r
-\r
- Copyright (c) 2014-2015 Datalight, Inc.\r
- All Rights Reserved Worldwide.\r
-\r
- This program is free software; you can redistribute it and/or modify\r
- it under the terms of the GNU General Public License as published by\r
- the Free Software Foundation; use version 2 of the License.\r
-\r
- This program is distributed in the hope that it will be useful,\r
- but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty\r
- of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License along\r
- with this program; if not, write to the Free Software Foundation, Inc.,\r
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
-*/\r
-/* Businesses and individuals that for commercial or other reasons cannot\r
- comply with the terms of the GPLv2 license may obtain a commercial license\r
- before incorporating Reliance Edge into proprietary software for\r
- distribution in any form. Visit http://www.datalight.com/reliance-edge for\r
- more information.\r
-*/\r
-/** @file\r
- @brief Implements a random number generator.\r
-*/\r
-#include <redfs.h>\r
-#include <redtestutils.h>\r
-\r
-\r
-/* This is the global seed used by the random number generator when the caller\r
- has not provided a seed to either the RedRand32() or RedRand64() functions.\r
-*/\r
-static uint64_t ullGlobalRandomNumberSeed;\r
-\r
-/* Whether the above seed has been initialized.\r
-*/\r
-static bool fGlobalSeedInited;\r
-\r
-\r
-/** @brief Set the global seed used by the random number generator.\r
-\r
- The global seed gets used when RedRand64() or RedRand32() are called with\r
- a NULL seed argument.\r
-\r
- @param ullSeed The value to use as the global RNG seed.\r
-*/\r
-void RedRandSeed(\r
- uint64_t ullSeed)\r
-{\r
- ullGlobalRandomNumberSeed = ullSeed;\r
- fGlobalSeedInited = true;\r
-}\r
-\r
-\r
-/** @brief Generate a 64-bit pseudo-random number.\r
-\r
- The period of this random number generator is 2^64 (1.8 x 1019). These\r
- parameters are the same as the default one-stream SPRNG lcg64 generator and\r
- it satisfies the requirements for a maximal period.\r
-\r
- The tempering value is used and an AND mask and is specifically selected to\r
- favor the distribution of lower bits.\r
-\r
- @param pullSeed A pointer to the seed to use. Set this value to NULL to\r
- use the internal global seed value.\r
-\r
- @return A pseudo-random number in the range [0, UINT64_MAX].\r
-*/\r
-uint64_t RedRand64(\r
- uint64_t *pullSeed)\r
-{\r
- const uint64_t ullA = UINT64_SUFFIX(2862933555777941757);\r
- const uint64_t ullC = UINT64_SUFFIX(3037000493);\r
- const uint64_t ullT = UINT64_SUFFIX(4921441182957829599);\r
- uint64_t ullN;\r
- uint64_t *pullSeedPtr;\r
- uint64_t ullLocalSeed;\r
-\r
- if(pullSeed != NULL)\r
- {\r
- ullLocalSeed = *pullSeed;\r
- pullSeedPtr = pullSeed;\r
- }\r
- else\r
- {\r
- if(!fGlobalSeedInited)\r
- {\r
- /* Unfortunately, the Reliance Edge OS services don't give us much\r
- to work with to initialize the global seed. There is no entropy\r
- abstraction, no tick count abstraction, and the timestamp\r
- abstraction uses an opaque type which is not guaranteed to be an\r
- integer. The best we can do is use the RTC.\r
-\r
- Tests using the RNG should be supplying a seed anyway, for\r
- reproducibility.\r
- */\r
- RedRandSeed((uint64_t)RedOsClockGetTime());\r
- }\r
-\r
- ullLocalSeed = ullGlobalRandomNumberSeed;\r
- pullSeedPtr = &ullGlobalRandomNumberSeed;\r
- }\r
-\r
- ullN = (ullLocalSeed * ullA) + ullC;\r
-\r
- *pullSeedPtr = ullN;\r
-\r
- /* The linear congruential generator used above produces good psuedo-random\r
- 64-bit number sequences, however, as with any LCG, the period of the\r
- lower order bits is much shorter resulting in alternately odd/even pairs\r
- in bit zero.\r
-\r
- The result of the LGC above is tempered below with a series of XOR and\r
- shift operations to produce a more acceptable equidistribution of bits\r
- throughout the 64-bit range.\r
- */\r
- ullN ^= (ullN >> 21U) & ullT;\r
- ullN ^= (ullN >> 43U) & ullT;\r
- ullN ^= (ullN << 23U) & ~ullT;\r
- ullN ^= (ullN << 31U) & ~ullT;\r
-\r
- return ullN;\r
-}\r
-\r
-\r
-/** @brief Generate a 32-bit pseudo-random number.\r
-\r
- @note The 32-bit random number generator internally uses the 64-bit random\r
- number generator, returning the low 32-bits of the pseudo-random\r
- 64-bit value.\r
-\r
- @param pulSeed A pointer to the seed to use. Set this value to NULL to use\r
- the internal global seed value.\r
-\r
- @return A pseudo-random number in the range [0, UINT32_MAX].\r
-*/\r
-uint32_t RedRand32(\r
- uint32_t *pulSeed)\r
-{\r
- uint64_t ullN;\r
-\r
- if(pulSeed != NULL)\r
- {\r
- uint64_t ullLocalSeed;\r
-\r
- ullLocalSeed = *pulSeed;\r
- ullN = RedRand64(&ullLocalSeed);\r
- *pulSeed = (uint32_t)ullLocalSeed;\r
- }\r
- else\r
- {\r
- ullN = RedRand64(NULL);\r
- }\r
-\r
- return (uint32_t)ullN;\r
-}\r
-\r
+/* ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
+
+ Copyright (c) 2014-2015 Datalight, Inc.
+ All Rights Reserved Worldwide.
+
+ 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; use version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but "AS-IS," 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+/* Businesses and individuals that for commercial or other reasons cannot
+ comply with the terms of the GPLv2 license may obtain a commercial license
+ before incorporating Reliance Edge into proprietary software for
+ distribution in any form. Visit http://www.datalight.com/reliance-edge for
+ more information.
+*/
+/** @file
+ @brief Implements a random number generator.
+*/
+#include <redfs.h>
+#include <redtestutils.h>
+
+
+/* This is the global seed used by the random number generator when the caller
+ has not provided a seed to either the RedRand32() or RedRand64() functions.
+*/
+static uint64_t ullGlobalRandomNumberSeed;
+
+/* Whether the above seed has been initialized.
+*/
+static bool fGlobalSeedInited;
+
+
+/** @brief Set the global seed used by the random number generator.
+
+ The global seed gets used when RedRand64() or RedRand32() are called with
+ a NULL seed argument.
+
+ @param ullSeed The value to use as the global RNG seed.
+*/
+void RedRandSeed(
+ uint64_t ullSeed)
+{
+ ullGlobalRandomNumberSeed = ullSeed;
+ fGlobalSeedInited = true;
+}
+
+
+/** @brief Generate a 64-bit pseudo-random number.
+
+ The period of this random number generator is 2^64 (1.8 x 1019). These
+ parameters are the same as the default one-stream SPRNG lcg64 generator and
+ it satisfies the requirements for a maximal period.
+
+ The tempering value is used and an AND mask and is specifically selected to
+ favor the distribution of lower bits.
+
+ @param pullSeed A pointer to the seed to use. Set this value to NULL to
+ use the internal global seed value.
+
+ @return A pseudo-random number in the range [0, UINT64_MAX].
+*/
+uint64_t RedRand64(
+ uint64_t *pullSeed)
+{
+ const uint64_t ullA = UINT64_SUFFIX(2862933555777941757);
+ const uint64_t ullC = UINT64_SUFFIX(3037000493);
+ const uint64_t ullT = UINT64_SUFFIX(4921441182957829599);
+ uint64_t ullN;
+ uint64_t *pullSeedPtr;
+ uint64_t ullLocalSeed;
+
+ if(pullSeed != NULL)
+ {
+ ullLocalSeed = *pullSeed;
+ pullSeedPtr = pullSeed;
+ }
+ else
+ {
+ if(!fGlobalSeedInited)
+ {
+ /* Unfortunately, the Reliance Edge OS services don't give us much
+ to work with to initialize the global seed. There is no entropy
+ abstraction, no tick count abstraction, and the timestamp
+ abstraction uses an opaque type which is not guaranteed to be an
+ integer. The best we can do is use the RTC.
+
+ Tests using the RNG should be supplying a seed anyway, for
+ reproducibility.
+ */
+ RedRandSeed((uint64_t)RedOsClockGetTime());
+ }
+
+ ullLocalSeed = ullGlobalRandomNumberSeed;
+ pullSeedPtr = &ullGlobalRandomNumberSeed;
+ }
+
+ ullN = (ullLocalSeed * ullA) + ullC;
+
+ *pullSeedPtr = ullN;
+
+ /* The linear congruential generator used above produces good psuedo-random
+ 64-bit number sequences, however, as with any LCG, the period of the
+ lower order bits is much shorter resulting in alternately odd/even pairs
+ in bit zero.
+
+ The result of the LGC above is tempered below with a series of XOR and
+ shift operations to produce a more acceptable equidistribution of bits
+ throughout the 64-bit range.
+ */
+ ullN ^= (ullN >> 21U) & ullT;
+ ullN ^= (ullN >> 43U) & ullT;
+ ullN ^= (ullN << 23U) & ~ullT;
+ ullN ^= (ullN << 31U) & ~ullT;
+
+ return ullN;
+}
+
+
+/** @brief Generate a 32-bit pseudo-random number.
+
+ @note The 32-bit random number generator internally uses the 64-bit random
+ number generator, returning the low 32-bits of the pseudo-random
+ 64-bit value.
+
+ @param pulSeed A pointer to the seed to use. Set this value to NULL to use
+ the internal global seed value.
+
+ @return A pseudo-random number in the range [0, UINT32_MAX].
+*/
+uint32_t RedRand32(
+ uint32_t *pulSeed)
+{
+ uint64_t ullN;
+
+ if(pulSeed != NULL)
+ {
+ uint64_t ullLocalSeed;
+
+ ullLocalSeed = *pulSeed;
+ ullN = RedRand64(&ullLocalSeed);
+ *pulSeed = (uint32_t)ullLocalSeed;
+ }
+ else
+ {
+ ullN = RedRand64(NULL);
+ }
+
+ return (uint32_t)ullN;
+}
+