2
0

FileUtilities.cpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. //===- Support/FileUtilities.cpp - File System Utilities ------------------===//
  2. //
  3. // The LLVM Compiler Infrastructure
  4. //
  5. // This file is distributed under the University of Illinois Open Source
  6. // License. See LICENSE.TXT for details.
  7. //
  8. //===----------------------------------------------------------------------===//
  9. //
  10. // This file implements a family of utility functions which are useful for doing
  11. // various things with files.
  12. //
  13. //===----------------------------------------------------------------------===//
  14. #include "llvm/Support/FileUtilities.h"
  15. #include "llvm/ADT/SmallString.h"
  16. #include "llvm/Support/MemoryBuffer.h"
  17. #include "llvm/Support/Path.h"
  18. #include "llvm/Support/raw_ostream.h"
  19. #include <cctype>
  20. #include <cstdlib>
  21. #include <cstring>
  22. #include <system_error>
  23. using namespace llvm;
  24. static bool isSignedChar(char C) {
  25. return (C == '+' || C == '-');
  26. }
  27. static bool isExponentChar(char C) {
  28. switch (C) {
  29. case 'D': // Strange exponential notation.
  30. case 'd': // Strange exponential notation.
  31. case 'e':
  32. case 'E': return true;
  33. default: return false;
  34. }
  35. }
  36. static bool isNumberChar(char C) {
  37. switch (C) {
  38. case '0': case '1': case '2': case '3': case '4':
  39. case '5': case '6': case '7': case '8': case '9':
  40. case '.': return true;
  41. default: return isSignedChar(C) || isExponentChar(C);
  42. }
  43. }
  44. static const char *BackupNumber(const char *Pos, const char *FirstChar) {
  45. // If we didn't stop in the middle of a number, don't backup.
  46. if (!isNumberChar(*Pos)) return Pos;
  47. // Otherwise, return to the start of the number.
  48. bool HasPeriod = false;
  49. while (Pos > FirstChar && isNumberChar(Pos[-1])) {
  50. // Backup over at most one period.
  51. if (Pos[-1] == '.') {
  52. if (HasPeriod)
  53. break;
  54. HasPeriod = true;
  55. }
  56. --Pos;
  57. if (Pos > FirstChar && isSignedChar(Pos[0]) && !isExponentChar(Pos[-1]))
  58. break;
  59. }
  60. return Pos;
  61. }
  62. /// EndOfNumber - Return the first character that is not part of the specified
  63. /// number. This assumes that the buffer is null terminated, so it won't fall
  64. /// off the end.
  65. static const char *EndOfNumber(const char *Pos) {
  66. while (isNumberChar(*Pos))
  67. ++Pos;
  68. return Pos;
  69. }
  70. /// CompareNumbers - compare two numbers, returning true if they are different.
  71. static bool CompareNumbers(const char *&F1P, const char *&F2P,
  72. const char *F1End, const char *F2End,
  73. double AbsTolerance, double RelTolerance,
  74. std::string *ErrorMsg) {
  75. const char *F1NumEnd, *F2NumEnd;
  76. double V1 = 0.0, V2 = 0.0;
  77. // If one of the positions is at a space and the other isn't, chomp up 'til
  78. // the end of the space.
  79. while (isspace(static_cast<unsigned char>(*F1P)) && F1P != F1End)
  80. ++F1P;
  81. while (isspace(static_cast<unsigned char>(*F2P)) && F2P != F2End)
  82. ++F2P;
  83. // If we stop on numbers, compare their difference.
  84. if (!isNumberChar(*F1P) || !isNumberChar(*F2P)) {
  85. // The diff failed.
  86. F1NumEnd = F1P;
  87. F2NumEnd = F2P;
  88. } else {
  89. // Note that some ugliness is built into this to permit support for numbers
  90. // that use "D" or "d" as their exponential marker, e.g. "1.234D45". This
  91. // occurs in 200.sixtrack in spec2k.
  92. V1 = strtod(F1P, const_cast<char**>(&F1NumEnd));
  93. V2 = strtod(F2P, const_cast<char**>(&F2NumEnd));
  94. if (*F1NumEnd == 'D' || *F1NumEnd == 'd') {
  95. // Copy string into tmp buffer to replace the 'D' with an 'e'.
  96. SmallString<200> StrTmp(F1P, EndOfNumber(F1NumEnd)+1);
  97. // Strange exponential notation!
  98. StrTmp[static_cast<unsigned>(F1NumEnd-F1P)] = 'e';
  99. V1 = strtod(&StrTmp[0], const_cast<char**>(&F1NumEnd));
  100. F1NumEnd = F1P + (F1NumEnd-&StrTmp[0]);
  101. }
  102. if (*F2NumEnd == 'D' || *F2NumEnd == 'd') {
  103. // Copy string into tmp buffer to replace the 'D' with an 'e'.
  104. SmallString<200> StrTmp(F2P, EndOfNumber(F2NumEnd)+1);
  105. // Strange exponential notation!
  106. StrTmp[static_cast<unsigned>(F2NumEnd-F2P)] = 'e';
  107. V2 = strtod(&StrTmp[0], const_cast<char**>(&F2NumEnd));
  108. F2NumEnd = F2P + (F2NumEnd-&StrTmp[0]);
  109. }
  110. }
  111. if (F1NumEnd == F1P || F2NumEnd == F2P) {
  112. if (ErrorMsg) {
  113. *ErrorMsg = "FP Comparison failed, not a numeric difference between '";
  114. *ErrorMsg += F1P[0];
  115. *ErrorMsg += "' and '";
  116. *ErrorMsg += F2P[0];
  117. *ErrorMsg += "'";
  118. }
  119. return true;
  120. }
  121. // Check to see if these are inside the absolute tolerance
  122. if (AbsTolerance < std::abs(V1-V2)) {
  123. // Nope, check the relative tolerance...
  124. double Diff;
  125. if (V2)
  126. Diff = std::abs(V1/V2 - 1.0);
  127. else if (V1)
  128. Diff = std::abs(V2/V1 - 1.0);
  129. else
  130. Diff = 0; // Both zero.
  131. if (Diff > RelTolerance) {
  132. if (ErrorMsg) {
  133. raw_string_ostream(*ErrorMsg)
  134. << "Compared: " << V1 << " and " << V2 << '\n'
  135. << "abs. diff = " << std::abs(V1-V2) << " rel.diff = " << Diff << '\n'
  136. << "Out of tolerance: rel/abs: " << RelTolerance << '/'
  137. << AbsTolerance;
  138. }
  139. return true;
  140. }
  141. }
  142. // Otherwise, advance our read pointers to the end of the numbers.
  143. F1P = F1NumEnd; F2P = F2NumEnd;
  144. return false;
  145. }
  146. /// DiffFilesWithTolerance - Compare the two files specified, returning 0 if the
  147. /// files match, 1 if they are different, and 2 if there is a file error. This
  148. /// function differs from DiffFiles in that you can specify an absolete and
  149. /// relative FP error that is allowed to exist. If you specify a string to fill
  150. /// in for the error option, it will set the string to an error message if an
  151. /// error occurs, allowing the caller to distinguish between a failed diff and a
  152. /// file system error.
  153. ///
  154. int llvm::DiffFilesWithTolerance(StringRef NameA,
  155. StringRef NameB,
  156. double AbsTol, double RelTol,
  157. std::string *Error) {
  158. // Now its safe to mmap the files into memory because both files
  159. // have a non-zero size.
  160. ErrorOr<std::unique_ptr<MemoryBuffer>> F1OrErr = MemoryBuffer::getFile(NameA);
  161. if (std::error_code EC = F1OrErr.getError()) {
  162. if (Error)
  163. *Error = EC.message();
  164. return 2;
  165. }
  166. MemoryBuffer &F1 = *F1OrErr.get();
  167. ErrorOr<std::unique_ptr<MemoryBuffer>> F2OrErr = MemoryBuffer::getFile(NameB);
  168. if (std::error_code EC = F2OrErr.getError()) {
  169. if (Error)
  170. *Error = EC.message();
  171. return 2;
  172. }
  173. MemoryBuffer &F2 = *F2OrErr.get();
  174. // Okay, now that we opened the files, scan them for the first difference.
  175. const char *File1Start = F1.getBufferStart();
  176. const char *File2Start = F2.getBufferStart();
  177. const char *File1End = F1.getBufferEnd();
  178. const char *File2End = F2.getBufferEnd();
  179. const char *F1P = File1Start;
  180. const char *F2P = File2Start;
  181. uint64_t A_size = F1.getBufferSize();
  182. uint64_t B_size = F2.getBufferSize();
  183. // Are the buffers identical? Common case: Handle this efficiently.
  184. if (A_size == B_size &&
  185. std::memcmp(File1Start, File2Start, A_size) == 0)
  186. return 0;
  187. // Otherwise, we are done a tolerances are set.
  188. if (AbsTol == 0 && RelTol == 0) {
  189. if (Error)
  190. *Error = "Files differ without tolerance allowance";
  191. return 1; // Files different!
  192. }
  193. bool CompareFailed = false;
  194. while (1) {
  195. // Scan for the end of file or next difference.
  196. while (F1P < File1End && F2P < File2End && *F1P == *F2P)
  197. ++F1P, ++F2P;
  198. if (F1P >= File1End || F2P >= File2End) break;
  199. // Okay, we must have found a difference. Backup to the start of the
  200. // current number each stream is at so that we can compare from the
  201. // beginning.
  202. F1P = BackupNumber(F1P, File1Start);
  203. F2P = BackupNumber(F2P, File2Start);
  204. // Now that we are at the start of the numbers, compare them, exiting if
  205. // they don't match.
  206. if (CompareNumbers(F1P, F2P, File1End, File2End, AbsTol, RelTol, Error)) {
  207. CompareFailed = true;
  208. break;
  209. }
  210. }
  211. // Okay, we reached the end of file. If both files are at the end, we
  212. // succeeded.
  213. bool F1AtEnd = F1P >= File1End;
  214. bool F2AtEnd = F2P >= File2End;
  215. if (!CompareFailed && (!F1AtEnd || !F2AtEnd)) {
  216. // Else, we might have run off the end due to a number: backup and retry.
  217. if (F1AtEnd && isNumberChar(F1P[-1])) --F1P;
  218. if (F2AtEnd && isNumberChar(F2P[-1])) --F2P;
  219. F1P = BackupNumber(F1P, File1Start);
  220. F2P = BackupNumber(F2P, File2Start);
  221. // Now that we are at the start of the numbers, compare them, exiting if
  222. // they don't match.
  223. if (CompareNumbers(F1P, F2P, File1End, File2End, AbsTol, RelTol, Error))
  224. CompareFailed = true;
  225. // If we found the end, we succeeded.
  226. if (F1P < File1End || F2P < File2End)
  227. CompareFailed = true;
  228. }
  229. return CompareFailed;
  230. }