mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-21 01:01:43 +00:00
Prepare wpiutil for merge into allwpilib.
This commit is contained in:
709
wpiutil/src/main/native/cpp/llvm/ConvertUTF.cpp
Normal file
709
wpiutil/src/main/native/cpp/llvm/ConvertUTF.cpp
Normal file
@@ -0,0 +1,709 @@
|
||||
/*===--- ConvertUTF.c - Universal Character Names conversions ---------------===
|
||||
*
|
||||
* The LLVM Compiler Infrastructure
|
||||
*
|
||||
* This file is distributed under the University of Illinois Open Source
|
||||
* License. See LICENSE.TXT for details.
|
||||
*
|
||||
*===------------------------------------------------------------------------=*/
|
||||
/*
|
||||
* Copyright 2001-2004 Unicode, Inc.
|
||||
*
|
||||
* Disclaimer
|
||||
*
|
||||
* This source code is provided as is by Unicode, Inc. No claims are
|
||||
* made as to fitness for any particular purpose. No warranties of any
|
||||
* kind are expressed or implied. The recipient agrees to determine
|
||||
* applicability of information provided. If this file has been
|
||||
* purchased on magnetic or optical media from Unicode, Inc., the
|
||||
* sole remedy for any claim will be exchange of defective media
|
||||
* within 90 days of receipt.
|
||||
*
|
||||
* Limitations on Rights to Redistribute This Code
|
||||
*
|
||||
* Unicode, Inc. hereby grants the right to freely use the information
|
||||
* supplied in this file in the creation of products supporting the
|
||||
* Unicode Standard, and to make copies of this file in any form
|
||||
* for internal or external distribution as long as this notice
|
||||
* remains attached.
|
||||
*/
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
|
||||
Conversions between UTF32, UTF-16, and UTF-8. Source code file.
|
||||
Author: Mark E. Davis, 1994.
|
||||
Rev History: Rick McGowan, fixes & updates May 2001.
|
||||
Sept 2001: fixed const & error conditions per
|
||||
mods suggested by S. Parent & A. Lillich.
|
||||
June 2002: Tim Dodd added detection and handling of incomplete
|
||||
source sequences, enhanced error detection, added casts
|
||||
to eliminate compiler warnings.
|
||||
July 2003: slight mods to back out aggressive FFFE detection.
|
||||
Jan 2004: updated switches in from-UTF8 conversions.
|
||||
Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions.
|
||||
|
||||
See the header file "ConvertUTF.h" for complete documentation.
|
||||
|
||||
------------------------------------------------------------------------ */
|
||||
|
||||
|
||||
#include "llvm/ConvertUTF.h"
|
||||
#ifdef CVTUTF_DEBUG
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#include <assert.h>
|
||||
|
||||
static const int halfShift = 10; /* used for shifting by 10 bits */
|
||||
|
||||
static const UTF32 halfBase = 0x0010000UL;
|
||||
static const UTF32 halfMask = 0x3FFUL;
|
||||
|
||||
#define UNI_SUR_HIGH_START (UTF32)0xD800
|
||||
#define UNI_SUR_HIGH_END (UTF32)0xDBFF
|
||||
#define UNI_SUR_LOW_START (UTF32)0xDC00
|
||||
#define UNI_SUR_LOW_END (UTF32)0xDFFF
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Index into the table below with the first byte of a UTF-8 sequence to
|
||||
* get the number of trailing bytes that are supposed to follow it.
|
||||
* Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
|
||||
* left as-is for anyone who may want to do such conversion, which was
|
||||
* allowed in earlier algorithms.
|
||||
*/
|
||||
static const char trailingBytesForUTF8[256] = {
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
|
||||
};
|
||||
|
||||
/*
|
||||
* Magic values subtracted from a buffer value during UTF8 conversion.
|
||||
* This table contains as many values as there might be trailing bytes
|
||||
* in a UTF-8 sequence.
|
||||
*/
|
||||
static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
|
||||
0x03C82080UL, 0xFA082080UL, 0x82082080UL };
|
||||
|
||||
/*
|
||||
* Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
|
||||
* into the first byte, depending on how many bytes follow. There are
|
||||
* as many entries in this table as there are UTF-8 sequence types.
|
||||
* (I.e., one byte sequence, two byte... etc.). Remember that sequencs
|
||||
* for *legal* UTF-8 will be 4 or fewer bytes total.
|
||||
*/
|
||||
static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/* The interface converts a whole buffer to avoid function-call overhead.
|
||||
* Constants have been gathered. Loops & conditionals have been removed as
|
||||
* much as possible for efficiency, in favor of drop-through switches.
|
||||
* (See "Note A" at the bottom of the file for equivalent code.)
|
||||
* If your compiler supports it, the "isLegalUTF8" call can be turned
|
||||
* into an inline function.
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
ConversionResult ConvertUTF32toUTF16 (
|
||||
const UTF32** sourceStart, const UTF32* sourceEnd,
|
||||
UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
|
||||
ConversionResult result = conversionOK;
|
||||
const UTF32* source = *sourceStart;
|
||||
UTF16* target = *targetStart;
|
||||
while (source < sourceEnd) {
|
||||
UTF32 ch;
|
||||
if (target >= targetEnd) {
|
||||
result = targetExhausted; break;
|
||||
}
|
||||
ch = *source++;
|
||||
if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
|
||||
/* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */
|
||||
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
|
||||
if (flags == strictConversion) {
|
||||
--source; /* return to the illegal value itself */
|
||||
result = sourceIllegal;
|
||||
break;
|
||||
} else {
|
||||
*target++ = UNI_REPLACEMENT_CHAR;
|
||||
}
|
||||
} else {
|
||||
*target++ = (UTF16)ch; /* normal case */
|
||||
}
|
||||
} else if (ch > UNI_MAX_LEGAL_UTF32) {
|
||||
if (flags == strictConversion) {
|
||||
result = sourceIllegal;
|
||||
} else {
|
||||
*target++ = UNI_REPLACEMENT_CHAR;
|
||||
}
|
||||
} else {
|
||||
/* target is a character in range 0xFFFF - 0x10FFFF. */
|
||||
if (target + 1 >= targetEnd) {
|
||||
--source; /* Back up source pointer! */
|
||||
result = targetExhausted; break;
|
||||
}
|
||||
ch -= halfBase;
|
||||
*target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
|
||||
*target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
|
||||
}
|
||||
}
|
||||
*sourceStart = source;
|
||||
*targetStart = target;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
ConversionResult ConvertUTF16toUTF32 (
|
||||
const UTF16** sourceStart, const UTF16* sourceEnd,
|
||||
UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) {
|
||||
ConversionResult result = conversionOK;
|
||||
const UTF16* source = *sourceStart;
|
||||
UTF32* target = *targetStart;
|
||||
UTF32 ch, ch2;
|
||||
while (source < sourceEnd) {
|
||||
const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
|
||||
ch = *source++;
|
||||
/* If we have a surrogate pair, convert to UTF32 first. */
|
||||
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
|
||||
/* If the 16 bits following the high surrogate are in the source buffer... */
|
||||
if (source < sourceEnd) {
|
||||
ch2 = *source;
|
||||
/* If it's a low surrogate, convert to UTF32. */
|
||||
if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
|
||||
ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
|
||||
+ (ch2 - UNI_SUR_LOW_START) + halfBase;
|
||||
++source;
|
||||
} else if (flags == strictConversion) { /* it's an unpaired high surrogate */
|
||||
--source; /* return to the illegal value itself */
|
||||
result = sourceIllegal;
|
||||
break;
|
||||
}
|
||||
} else { /* We don't have the 16 bits following the high surrogate. */
|
||||
--source; /* return to the high surrogate */
|
||||
result = sourceExhausted;
|
||||
break;
|
||||
}
|
||||
} else if (flags == strictConversion) {
|
||||
/* UTF-16 surrogate values are illegal in UTF-32 */
|
||||
if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
|
||||
--source; /* return to the illegal value itself */
|
||||
result = sourceIllegal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (target >= targetEnd) {
|
||||
source = oldSource; /* Back up source pointer! */
|
||||
result = targetExhausted; break;
|
||||
}
|
||||
*target++ = ch;
|
||||
}
|
||||
*sourceStart = source;
|
||||
*targetStart = target;
|
||||
#ifdef CVTUTF_DEBUG
|
||||
if (result == sourceIllegal) {
|
||||
fprintf(stderr, "ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2);
|
||||
fflush(stderr);
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
ConversionResult ConvertUTF16toUTF8 (
|
||||
const UTF16** sourceStart, const UTF16* sourceEnd,
|
||||
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
|
||||
ConversionResult result = conversionOK;
|
||||
const UTF16* source = *sourceStart;
|
||||
UTF8* target = *targetStart;
|
||||
while (source < sourceEnd) {
|
||||
UTF32 ch;
|
||||
unsigned short bytesToWrite = 0;
|
||||
const UTF32 byteMask = 0xBF;
|
||||
const UTF32 byteMark = 0x80;
|
||||
const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
|
||||
ch = *source++;
|
||||
/* If we have a surrogate pair, convert to UTF32 first. */
|
||||
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
|
||||
/* If the 16 bits following the high surrogate are in the source buffer... */
|
||||
if (source < sourceEnd) {
|
||||
UTF32 ch2 = *source;
|
||||
/* If it's a low surrogate, convert to UTF32. */
|
||||
if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
|
||||
ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
|
||||
+ (ch2 - UNI_SUR_LOW_START) + halfBase;
|
||||
++source;
|
||||
} else if (flags == strictConversion) { /* it's an unpaired high surrogate */
|
||||
--source; /* return to the illegal value itself */
|
||||
result = sourceIllegal;
|
||||
break;
|
||||
}
|
||||
} else { /* We don't have the 16 bits following the high surrogate. */
|
||||
--source; /* return to the high surrogate */
|
||||
result = sourceExhausted;
|
||||
break;
|
||||
}
|
||||
} else if (flags == strictConversion) {
|
||||
/* UTF-16 surrogate values are illegal in UTF-32 */
|
||||
if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
|
||||
--source; /* return to the illegal value itself */
|
||||
result = sourceIllegal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Figure out how many bytes the result will require */
|
||||
if (ch < (UTF32)0x80) { bytesToWrite = 1;
|
||||
} else if (ch < (UTF32)0x800) { bytesToWrite = 2;
|
||||
} else if (ch < (UTF32)0x10000) { bytesToWrite = 3;
|
||||
} else if (ch < (UTF32)0x110000) { bytesToWrite = 4;
|
||||
} else { bytesToWrite = 3;
|
||||
ch = UNI_REPLACEMENT_CHAR;
|
||||
}
|
||||
|
||||
target += bytesToWrite;
|
||||
if (target > targetEnd) {
|
||||
source = oldSource; /* Back up source pointer! */
|
||||
target -= bytesToWrite; result = targetExhausted; break;
|
||||
}
|
||||
switch (bytesToWrite) { /* note: everything falls through. */
|
||||
case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; /* FALLTHRU */
|
||||
case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; /* FALLTHRU */
|
||||
case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; /* FALLTHRU */
|
||||
case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]);
|
||||
}
|
||||
target += bytesToWrite;
|
||||
}
|
||||
*sourceStart = source;
|
||||
*targetStart = target;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
ConversionResult ConvertUTF32toUTF8 (
|
||||
const UTF32** sourceStart, const UTF32* sourceEnd,
|
||||
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
|
||||
ConversionResult result = conversionOK;
|
||||
const UTF32* source = *sourceStart;
|
||||
UTF8* target = *targetStart;
|
||||
while (source < sourceEnd) {
|
||||
UTF32 ch;
|
||||
unsigned short bytesToWrite = 0;
|
||||
const UTF32 byteMask = 0xBF;
|
||||
const UTF32 byteMark = 0x80;
|
||||
ch = *source++;
|
||||
if (flags == strictConversion ) {
|
||||
/* UTF-16 surrogate values are illegal in UTF-32 */
|
||||
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
|
||||
--source; /* return to the illegal value itself */
|
||||
result = sourceIllegal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Figure out how many bytes the result will require. Turn any
|
||||
* illegally large UTF32 things (> Plane 17) into replacement chars.
|
||||
*/
|
||||
if (ch < (UTF32)0x80) { bytesToWrite = 1;
|
||||
} else if (ch < (UTF32)0x800) { bytesToWrite = 2;
|
||||
} else if (ch < (UTF32)0x10000) { bytesToWrite = 3;
|
||||
} else if (ch <= UNI_MAX_LEGAL_UTF32) { bytesToWrite = 4;
|
||||
} else { bytesToWrite = 3;
|
||||
ch = UNI_REPLACEMENT_CHAR;
|
||||
result = sourceIllegal;
|
||||
}
|
||||
|
||||
target += bytesToWrite;
|
||||
if (target > targetEnd) {
|
||||
--source; /* Back up source pointer! */
|
||||
target -= bytesToWrite; result = targetExhausted; break;
|
||||
}
|
||||
switch (bytesToWrite) { /* note: everything falls through. */
|
||||
case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; /* FALLTHRU */
|
||||
case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; /* FALLTHRU */
|
||||
case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; /* FALLTHRU */
|
||||
case 1: *--target = (UTF8) (ch | firstByteMark[bytesToWrite]);
|
||||
}
|
||||
target += bytesToWrite;
|
||||
}
|
||||
*sourceStart = source;
|
||||
*targetStart = target;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Utility routine to tell whether a sequence of bytes is legal UTF-8.
|
||||
* This must be called with the length pre-determined by the first byte.
|
||||
* If not calling this from ConvertUTF8to*, then the length can be set by:
|
||||
* length = trailingBytesForUTF8[*source]+1;
|
||||
* and the sequence is illegal right away if there aren't that many bytes
|
||||
* available.
|
||||
* If presented with a length > 4, this returns false. The Unicode
|
||||
* definition of UTF-8 goes up to 4-byte sequences.
|
||||
*/
|
||||
|
||||
static Boolean isLegalUTF8(const UTF8 *source, int length) {
|
||||
UTF8 a;
|
||||
const UTF8 *srcptr = source+length;
|
||||
switch (length) {
|
||||
default: return false;
|
||||
/* Everything else falls through when "true"... */
|
||||
case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; /* FALLTHRU */
|
||||
case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; /* FALLTHRU */
|
||||
case 2: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
|
||||
|
||||
switch (*source) {
|
||||
/* no fall-through in this inner switch */
|
||||
case 0xE0: if (a < 0xA0) return false; break;
|
||||
case 0xED: if (a > 0x9F) return false; break;
|
||||
case 0xF0: if (a < 0x90) return false; break;
|
||||
case 0xF4: if (a > 0x8F) return false; break;
|
||||
default: if (a < 0x80) return false;
|
||||
}
|
||||
/* FALLTHRU */
|
||||
|
||||
case 1: if (*source >= 0x80 && *source < 0xC2) return false;
|
||||
}
|
||||
if (*source > 0xF4) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Exported function to return whether a UTF-8 sequence is legal or not.
|
||||
* This is not used here; it's just exported.
|
||||
*/
|
||||
Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) {
|
||||
int length = trailingBytesForUTF8[*source]+1;
|
||||
if (length > sourceEnd - source) {
|
||||
return false;
|
||||
}
|
||||
return isLegalUTF8(source, length);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static unsigned
|
||||
findMaximalSubpartOfIllFormedUTF8Sequence(const UTF8 *source,
|
||||
const UTF8 *sourceEnd) {
|
||||
UTF8 b1, b2, b3;
|
||||
|
||||
assert(!isLegalUTF8Sequence(source, sourceEnd));
|
||||
|
||||
/*
|
||||
* Unicode 6.3.0, D93b:
|
||||
*
|
||||
* Maximal subpart of an ill-formed subsequence: The longest code unit
|
||||
* subsequence starting at an unconvertible offset that is either:
|
||||
* a. the initial subsequence of a well-formed code unit sequence, or
|
||||
* b. a subsequence of length one.
|
||||
*/
|
||||
|
||||
if (source == sourceEnd)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Perform case analysis. See Unicode 6.3.0, Table 3-7. Well-Formed UTF-8
|
||||
* Byte Sequences.
|
||||
*/
|
||||
|
||||
b1 = *source;
|
||||
++source;
|
||||
if (b1 >= 0xC2 && b1 <= 0xDF) {
|
||||
/*
|
||||
* First byte is valid, but we know that this code unit sequence is
|
||||
* invalid, so the maximal subpart has to end after the first byte.
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (source == sourceEnd)
|
||||
return 1;
|
||||
|
||||
b2 = *source;
|
||||
++source;
|
||||
|
||||
if (b1 == 0xE0) {
|
||||
return (b2 >= 0xA0 && b2 <= 0xBF) ? 2 : 1;
|
||||
}
|
||||
if (b1 >= 0xE1 && b1 <= 0xEC) {
|
||||
return (b2 >= 0x80 && b2 <= 0xBF) ? 2 : 1;
|
||||
}
|
||||
if (b1 == 0xED) {
|
||||
return (b2 >= 0x80 && b2 <= 0x9F) ? 2 : 1;
|
||||
}
|
||||
if (b1 >= 0xEE && b1 <= 0xEF) {
|
||||
return (b2 >= 0x80 && b2 <= 0xBF) ? 2 : 1;
|
||||
}
|
||||
if (b1 == 0xF0) {
|
||||
if (b2 >= 0x90 && b2 <= 0xBF) {
|
||||
if (source == sourceEnd)
|
||||
return 2;
|
||||
|
||||
b3 = *source;
|
||||
return (b3 >= 0x80 && b3 <= 0xBF) ? 3 : 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (b1 >= 0xF1 && b1 <= 0xF3) {
|
||||
if (b2 >= 0x80 && b2 <= 0xBF) {
|
||||
if (source == sourceEnd)
|
||||
return 2;
|
||||
|
||||
b3 = *source;
|
||||
return (b3 >= 0x80 && b3 <= 0xBF) ? 3 : 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (b1 == 0xF4) {
|
||||
if (b2 >= 0x80 && b2 <= 0x8F) {
|
||||
if (source == sourceEnd)
|
||||
return 2;
|
||||
|
||||
b3 = *source;
|
||||
return (b3 >= 0x80 && b3 <= 0xBF) ? 3 : 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
assert((b1 >= 0x80 && b1 <= 0xC1) || b1 >= 0xF5);
|
||||
/*
|
||||
* There are no valid sequences that start with these bytes. Maximal subpart
|
||||
* is defined to have length 1 in these cases.
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Exported function to return the total number of bytes in a codepoint
|
||||
* represented in UTF-8, given the value of the first byte.
|
||||
*/
|
||||
unsigned getNumBytesForUTF8(UTF8 first) {
|
||||
return trailingBytesForUTF8[first] + 1;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Exported function to return whether a UTF-8 string is legal or not.
|
||||
* This is not used here; it's just exported.
|
||||
*/
|
||||
Boolean isLegalUTF8String(const UTF8 **source, const UTF8 *sourceEnd) {
|
||||
while (*source != sourceEnd) {
|
||||
int length = trailingBytesForUTF8[**source] + 1;
|
||||
if (length > sourceEnd - *source || !isLegalUTF8(*source, length))
|
||||
return false;
|
||||
*source += length;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
ConversionResult ConvertUTF8toUTF16 (
|
||||
const UTF8** sourceStart, const UTF8* sourceEnd,
|
||||
UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
|
||||
ConversionResult result = conversionOK;
|
||||
const UTF8* source = *sourceStart;
|
||||
UTF16* target = *targetStart;
|
||||
while (source < sourceEnd) {
|
||||
UTF32 ch = 0;
|
||||
unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
|
||||
if (extraBytesToRead >= sourceEnd - source) {
|
||||
result = sourceExhausted; break;
|
||||
}
|
||||
/* Do this check whether lenient or strict */
|
||||
if (!isLegalUTF8(source, extraBytesToRead+1)) {
|
||||
result = sourceIllegal;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* The cases all fall through. See "Note A" below.
|
||||
*/
|
||||
switch (extraBytesToRead) {
|
||||
case 5: ch += *source++; ch <<= 6; /* FALLTHRU */ /* remember, illegal UTF-8 */
|
||||
case 4: ch += *source++; ch <<= 6; /* FALLTHRU */ /* remember, illegal UTF-8 */
|
||||
case 3: ch += *source++; ch <<= 6; /* FALLTHRU */
|
||||
case 2: ch += *source++; ch <<= 6; /* FALLTHRU */
|
||||
case 1: ch += *source++; ch <<= 6; /* FALLTHRU */
|
||||
case 0: ch += *source++;
|
||||
}
|
||||
ch -= offsetsFromUTF8[extraBytesToRead];
|
||||
|
||||
if (target >= targetEnd) {
|
||||
source -= (extraBytesToRead+1); /* Back up source pointer! */
|
||||
result = targetExhausted; break;
|
||||
}
|
||||
if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
|
||||
/* UTF-16 surrogate values are illegal in UTF-32 */
|
||||
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
|
||||
if (flags == strictConversion) {
|
||||
source -= (extraBytesToRead+1); /* return to the illegal value itself */
|
||||
result = sourceIllegal;
|
||||
break;
|
||||
} else {
|
||||
*target++ = UNI_REPLACEMENT_CHAR;
|
||||
}
|
||||
} else {
|
||||
*target++ = (UTF16)ch; /* normal case */
|
||||
}
|
||||
} else if (ch > UNI_MAX_UTF16) {
|
||||
if (flags == strictConversion) {
|
||||
result = sourceIllegal;
|
||||
source -= (extraBytesToRead+1); /* return to the start */
|
||||
break; /* Bail out; shouldn't continue */
|
||||
} else {
|
||||
*target++ = UNI_REPLACEMENT_CHAR;
|
||||
}
|
||||
} else {
|
||||
/* target is a character in range 0xFFFF - 0x10FFFF. */
|
||||
if (target + 1 >= targetEnd) {
|
||||
source -= (extraBytesToRead+1); /* Back up source pointer! */
|
||||
result = targetExhausted; break;
|
||||
}
|
||||
ch -= halfBase;
|
||||
*target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
|
||||
*target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
|
||||
}
|
||||
}
|
||||
*sourceStart = source;
|
||||
*targetStart = target;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
static ConversionResult ConvertUTF8toUTF32Impl(
|
||||
const UTF8** sourceStart, const UTF8* sourceEnd,
|
||||
UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags,
|
||||
Boolean InputIsPartial) {
|
||||
ConversionResult result = conversionOK;
|
||||
const UTF8* source = *sourceStart;
|
||||
UTF32* target = *targetStart;
|
||||
while (source < sourceEnd) {
|
||||
UTF32 ch = 0;
|
||||
unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
|
||||
if (extraBytesToRead >= sourceEnd - source) {
|
||||
if (flags == strictConversion || InputIsPartial) {
|
||||
result = sourceExhausted;
|
||||
break;
|
||||
} else {
|
||||
result = sourceIllegal;
|
||||
|
||||
/*
|
||||
* Replace the maximal subpart of ill-formed sequence with
|
||||
* replacement character.
|
||||
*/
|
||||
source += findMaximalSubpartOfIllFormedUTF8Sequence(source,
|
||||
sourceEnd);
|
||||
*target++ = UNI_REPLACEMENT_CHAR;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (target >= targetEnd) {
|
||||
result = targetExhausted; break;
|
||||
}
|
||||
|
||||
/* Do this check whether lenient or strict */
|
||||
if (!isLegalUTF8(source, extraBytesToRead+1)) {
|
||||
result = sourceIllegal;
|
||||
if (flags == strictConversion) {
|
||||
/* Abort conversion. */
|
||||
break;
|
||||
} else {
|
||||
/*
|
||||
* Replace the maximal subpart of ill-formed sequence with
|
||||
* replacement character.
|
||||
*/
|
||||
source += findMaximalSubpartOfIllFormedUTF8Sequence(source,
|
||||
sourceEnd);
|
||||
*target++ = UNI_REPLACEMENT_CHAR;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* The cases all fall through. See "Note A" below.
|
||||
*/
|
||||
switch (extraBytesToRead) {
|
||||
case 5: ch += *source++; ch <<= 6; /* FALLTHRU */
|
||||
case 4: ch += *source++; ch <<= 6; /* FALLTHRU */
|
||||
case 3: ch += *source++; ch <<= 6; /* FALLTHRU */
|
||||
case 2: ch += *source++; ch <<= 6; /* FALLTHRU */
|
||||
case 1: ch += *source++; ch <<= 6; /* FALLTHRU */
|
||||
case 0: ch += *source++;
|
||||
}
|
||||
ch -= offsetsFromUTF8[extraBytesToRead];
|
||||
|
||||
if (ch <= UNI_MAX_LEGAL_UTF32) {
|
||||
/*
|
||||
* UTF-16 surrogate values are illegal in UTF-32, and anything
|
||||
* over Plane 17 (> 0x10FFFF) is illegal.
|
||||
*/
|
||||
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
|
||||
if (flags == strictConversion) {
|
||||
source -= (extraBytesToRead+1); /* return to the illegal value itself */
|
||||
result = sourceIllegal;
|
||||
break;
|
||||
} else {
|
||||
*target++ = UNI_REPLACEMENT_CHAR;
|
||||
}
|
||||
} else {
|
||||
*target++ = ch;
|
||||
}
|
||||
} else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */
|
||||
result = sourceIllegal;
|
||||
*target++ = UNI_REPLACEMENT_CHAR;
|
||||
}
|
||||
}
|
||||
*sourceStart = source;
|
||||
*targetStart = target;
|
||||
return result;
|
||||
}
|
||||
|
||||
ConversionResult ConvertUTF8toUTF32Partial(const UTF8 **sourceStart,
|
||||
const UTF8 *sourceEnd,
|
||||
UTF32 **targetStart,
|
||||
UTF32 *targetEnd,
|
||||
ConversionFlags flags) {
|
||||
return ConvertUTF8toUTF32Impl(sourceStart, sourceEnd, targetStart, targetEnd,
|
||||
flags, /*InputIsPartial=*/true);
|
||||
}
|
||||
|
||||
ConversionResult ConvertUTF8toUTF32(const UTF8 **sourceStart,
|
||||
const UTF8 *sourceEnd, UTF32 **targetStart,
|
||||
UTF32 *targetEnd, ConversionFlags flags) {
|
||||
return ConvertUTF8toUTF32Impl(sourceStart, sourceEnd, targetStart, targetEnd,
|
||||
flags, /*InputIsPartial=*/false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
|
||||
Note A.
|
||||
The fall-through switches in UTF-8 reading code save a
|
||||
temp variable, some decrements & conditionals. The switches
|
||||
are equivalent to the following loop:
|
||||
{
|
||||
int tmpBytesToRead = extraBytesToRead+1;
|
||||
do {
|
||||
ch += *source++;
|
||||
--tmpBytesToRead;
|
||||
if (tmpBytesToRead) ch <<= 6;
|
||||
} while (tmpBytesToRead > 0);
|
||||
}
|
||||
In UTF-8 writing code, the switches on "bytesToWrite" are
|
||||
similarly unrolled loops.
|
||||
|
||||
--------------------------------------------------------------------- */
|
||||
122
wpiutil/src/main/native/cpp/llvm/ConvertUTFWrapper.cpp
Normal file
122
wpiutil/src/main/native/cpp/llvm/ConvertUTFWrapper.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
//===-- ConvertUTFWrapper.cpp - Wrap ConvertUTF.h with clang data types -----===
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/ConvertUTF.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace llvm {
|
||||
|
||||
bool ConvertCodePointToUTF8(unsigned Source, char *&ResultPtr) {
|
||||
const UTF32 *SourceStart = &Source;
|
||||
const UTF32 *SourceEnd = SourceStart + 1;
|
||||
UTF8 *TargetStart = reinterpret_cast<UTF8 *>(ResultPtr);
|
||||
UTF8 *TargetEnd = TargetStart + 4;
|
||||
ConversionResult CR = ConvertUTF32toUTF8(&SourceStart, SourceEnd,
|
||||
&TargetStart, TargetEnd,
|
||||
strictConversion);
|
||||
if (CR != conversionOK)
|
||||
return false;
|
||||
|
||||
ResultPtr = reinterpret_cast<char*>(TargetStart);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hasUTF16ByteOrderMark(ArrayRef<char> S) {
|
||||
return (S.size() >= 2 &&
|
||||
((S[0] == '\xff' && S[1] == '\xfe') ||
|
||||
(S[0] == '\xfe' && S[1] == '\xff')));
|
||||
}
|
||||
|
||||
bool convertUTF16ToUTF8String(ArrayRef<UTF16> SrcUTF16,
|
||||
SmallVectorImpl<char> &DstUTF8) {
|
||||
assert(DstUTF8.empty());
|
||||
|
||||
// Avoid OOB by returning early on empty input.
|
||||
if (SrcUTF16.empty())
|
||||
return true;
|
||||
|
||||
const UTF16 *Src = SrcUTF16.begin();
|
||||
const UTF16 *SrcEnd = SrcUTF16.end();
|
||||
|
||||
// Byteswap if necessary.
|
||||
std::vector<UTF16> ByteSwapped;
|
||||
if (Src[0] == UNI_UTF16_BYTE_ORDER_MARK_SWAPPED) {
|
||||
ByteSwapped.insert(ByteSwapped.end(), Src, SrcEnd);
|
||||
for (unsigned I = 0, E = ByteSwapped.size(); I != E; ++I)
|
||||
ByteSwapped[I] = (ByteSwapped[I] << 8) | (ByteSwapped[I] >> 8);
|
||||
Src = &ByteSwapped[0];
|
||||
SrcEnd = &ByteSwapped[ByteSwapped.size() - 1] + 1;
|
||||
}
|
||||
|
||||
// Skip the BOM for conversion.
|
||||
if (Src[0] == UNI_UTF16_BYTE_ORDER_MARK_NATIVE)
|
||||
Src++;
|
||||
|
||||
// Just allocate enough space up front. We'll shrink it later. Allocate
|
||||
// enough that we can fit a null terminator without reallocating.
|
||||
DstUTF8.resize(SrcUTF16.size() * UNI_MAX_UTF8_BYTES_PER_CODE_POINT + 1);
|
||||
UTF8 *Dst = reinterpret_cast<UTF8*>(&DstUTF8[0]);
|
||||
UTF8 *DstEnd = Dst + DstUTF8.size();
|
||||
|
||||
ConversionResult CR =
|
||||
ConvertUTF16toUTF8(&Src, SrcEnd, &Dst, DstEnd, strictConversion);
|
||||
assert(CR != targetExhausted);
|
||||
|
||||
if (CR != conversionOK) {
|
||||
DstUTF8.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
DstUTF8.resize(reinterpret_cast<char*>(Dst) - &DstUTF8[0]);
|
||||
DstUTF8.push_back(0);
|
||||
DstUTF8.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool convertUTF8ToUTF16String(StringRef SrcUTF8,
|
||||
SmallVectorImpl<UTF16> &DstUTF16) {
|
||||
assert(DstUTF16.empty());
|
||||
|
||||
// Avoid OOB by returning early on empty input.
|
||||
if (SrcUTF8.empty()) {
|
||||
DstUTF16.push_back(0);
|
||||
DstUTF16.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
const UTF8 *Src = reinterpret_cast<const UTF8 *>(SrcUTF8.begin());
|
||||
const UTF8 *SrcEnd = reinterpret_cast<const UTF8 *>(SrcUTF8.end());
|
||||
|
||||
// Allocate the same number of UTF-16 code units as UTF-8 code units. Encoding
|
||||
// as UTF-16 should always require the same amount or less code units than the
|
||||
// UTF-8 encoding. Allocate one extra byte for the null terminator though,
|
||||
// so that someone calling DstUTF16.data() gets a null terminated string.
|
||||
// We resize down later so we don't have to worry that this over allocates.
|
||||
DstUTF16.resize(SrcUTF8.size()+1);
|
||||
UTF16 *Dst = &DstUTF16[0];
|
||||
UTF16 *DstEnd = Dst + DstUTF16.size();
|
||||
|
||||
ConversionResult CR =
|
||||
ConvertUTF8toUTF16(&Src, SrcEnd, &Dst, DstEnd, strictConversion);
|
||||
assert(CR != targetExhausted);
|
||||
|
||||
if (CR != conversionOK) {
|
||||
DstUTF16.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
DstUTF16.resize(Dst - &DstUTF16[0]);
|
||||
DstUTF16.push_back(0);
|
||||
DstUTF16.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // end namespace llvm
|
||||
|
||||
83
wpiutil/src/main/native/cpp/llvm/ErrorHandling.cpp
Normal file
83
wpiutil/src/main/native/cpp/llvm/ErrorHandling.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
//===- lib/Support/ErrorHandling.cpp - Callbacks for errors ---------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines an API used to indicate fatal error conditions. Non-fatal
|
||||
// errors (most of them) should be handled through LLVMContext.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/WindowsError.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <system_error>
|
||||
#include <winerror.h>
|
||||
|
||||
// I'd rather not double the line count of the following.
|
||||
#define MAP_ERR_TO_COND(x, y) \
|
||||
case x: \
|
||||
return std::make_error_code(std::errc::y)
|
||||
|
||||
std::error_code llvm::mapWindowsError(unsigned EV) {
|
||||
switch (EV) {
|
||||
MAP_ERR_TO_COND(ERROR_ACCESS_DENIED, permission_denied);
|
||||
MAP_ERR_TO_COND(ERROR_ALREADY_EXISTS, file_exists);
|
||||
MAP_ERR_TO_COND(ERROR_BAD_UNIT, no_such_device);
|
||||
MAP_ERR_TO_COND(ERROR_BUFFER_OVERFLOW, filename_too_long);
|
||||
MAP_ERR_TO_COND(ERROR_BUSY, device_or_resource_busy);
|
||||
MAP_ERR_TO_COND(ERROR_BUSY_DRIVE, device_or_resource_busy);
|
||||
MAP_ERR_TO_COND(ERROR_CANNOT_MAKE, permission_denied);
|
||||
MAP_ERR_TO_COND(ERROR_CANTOPEN, io_error);
|
||||
MAP_ERR_TO_COND(ERROR_CANTREAD, io_error);
|
||||
MAP_ERR_TO_COND(ERROR_CANTWRITE, io_error);
|
||||
MAP_ERR_TO_COND(ERROR_CURRENT_DIRECTORY, permission_denied);
|
||||
MAP_ERR_TO_COND(ERROR_DEV_NOT_EXIST, no_such_device);
|
||||
MAP_ERR_TO_COND(ERROR_DEVICE_IN_USE, device_or_resource_busy);
|
||||
MAP_ERR_TO_COND(ERROR_DIR_NOT_EMPTY, directory_not_empty);
|
||||
MAP_ERR_TO_COND(ERROR_DIRECTORY, invalid_argument);
|
||||
MAP_ERR_TO_COND(ERROR_DISK_FULL, no_space_on_device);
|
||||
MAP_ERR_TO_COND(ERROR_FILE_EXISTS, file_exists);
|
||||
MAP_ERR_TO_COND(ERROR_FILE_NOT_FOUND, no_such_file_or_directory);
|
||||
MAP_ERR_TO_COND(ERROR_HANDLE_DISK_FULL, no_space_on_device);
|
||||
MAP_ERR_TO_COND(ERROR_INVALID_ACCESS, permission_denied);
|
||||
MAP_ERR_TO_COND(ERROR_INVALID_DRIVE, no_such_device);
|
||||
MAP_ERR_TO_COND(ERROR_INVALID_FUNCTION, function_not_supported);
|
||||
MAP_ERR_TO_COND(ERROR_INVALID_HANDLE, invalid_argument);
|
||||
MAP_ERR_TO_COND(ERROR_INVALID_NAME, invalid_argument);
|
||||
MAP_ERR_TO_COND(ERROR_LOCK_VIOLATION, no_lock_available);
|
||||
MAP_ERR_TO_COND(ERROR_LOCKED, no_lock_available);
|
||||
MAP_ERR_TO_COND(ERROR_NEGATIVE_SEEK, invalid_argument);
|
||||
MAP_ERR_TO_COND(ERROR_NOACCESS, permission_denied);
|
||||
MAP_ERR_TO_COND(ERROR_NOT_ENOUGH_MEMORY, not_enough_memory);
|
||||
MAP_ERR_TO_COND(ERROR_NOT_READY, resource_unavailable_try_again);
|
||||
MAP_ERR_TO_COND(ERROR_OPEN_FAILED, io_error);
|
||||
MAP_ERR_TO_COND(ERROR_OPEN_FILES, device_or_resource_busy);
|
||||
MAP_ERR_TO_COND(ERROR_OUTOFMEMORY, not_enough_memory);
|
||||
MAP_ERR_TO_COND(ERROR_PATH_NOT_FOUND, no_such_file_or_directory);
|
||||
MAP_ERR_TO_COND(ERROR_BAD_NETPATH, no_such_file_or_directory);
|
||||
MAP_ERR_TO_COND(ERROR_READ_FAULT, io_error);
|
||||
MAP_ERR_TO_COND(ERROR_RETRY, resource_unavailable_try_again);
|
||||
MAP_ERR_TO_COND(ERROR_SEEK, io_error);
|
||||
MAP_ERR_TO_COND(ERROR_SHARING_VIOLATION, permission_denied);
|
||||
MAP_ERR_TO_COND(ERROR_TOO_MANY_OPEN_FILES, too_many_files_open);
|
||||
MAP_ERR_TO_COND(ERROR_WRITE_FAULT, io_error);
|
||||
MAP_ERR_TO_COND(ERROR_WRITE_PROTECT, permission_denied);
|
||||
MAP_ERR_TO_COND(WSAEACCES, permission_denied);
|
||||
MAP_ERR_TO_COND(WSAEBADF, bad_file_descriptor);
|
||||
MAP_ERR_TO_COND(WSAEFAULT, bad_address);
|
||||
MAP_ERR_TO_COND(WSAEINTR, interrupted);
|
||||
MAP_ERR_TO_COND(WSAEINVAL, invalid_argument);
|
||||
MAP_ERR_TO_COND(WSAEMFILE, too_many_files_open);
|
||||
MAP_ERR_TO_COND(WSAENAMETOOLONG, filename_too_long);
|
||||
default:
|
||||
return std::error_code(EV, std::system_category());
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
29
wpiutil/src/main/native/cpp/llvm/Hashing.cpp
Normal file
29
wpiutil/src/main/native/cpp/llvm/Hashing.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
//===-------------- lib/Support/Hashing.cpp -------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file provides implementation bits for the LLVM common hashing
|
||||
// infrastructure. Documentation and most of the other information is in the
|
||||
// header file.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/Hashing.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
// Provide a definition and static initializer for the fixed seed. This
|
||||
// initializer should always be zero to ensure its value can never appear to be
|
||||
// non-zero, even during dynamic initialization.
|
||||
size_t llvm::hashing::detail::fixed_seed_override = 0;
|
||||
|
||||
// Implement the function for forced setting of the fixed seed.
|
||||
// FIXME: Use atomic operations here so that there is no data race.
|
||||
void llvm::set_fixed_execution_hash_seed(size_t fixed_value) {
|
||||
hashing::detail::fixed_seed_override = fixed_value;
|
||||
}
|
||||
822
wpiutil/src/main/native/cpp/llvm/Path.cpp
Normal file
822
wpiutil/src/main/native/cpp/llvm/Path.cpp
Normal file
@@ -0,0 +1,822 @@
|
||||
//===-- Path.cpp - Implement OS Path Concept ------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements the operating system Path API.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/Path.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
|
||||
#if !defined(_MSC_VER) && !defined(__MINGW32__)
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#include <io.h>
|
||||
#endif
|
||||
|
||||
#include "llvm/FileSystem.h"
|
||||
#include "llvm/SmallString.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace {
|
||||
using llvm::StringRef;
|
||||
using llvm::sys::path::is_separator;
|
||||
|
||||
#ifdef _WIN32
|
||||
const char *separators = "\\/";
|
||||
const char preferred_separator = '\\';
|
||||
#else
|
||||
const char separators = '/';
|
||||
const char preferred_separator = '/';
|
||||
#endif
|
||||
|
||||
StringRef find_first_component(StringRef path) {
|
||||
// Look for this first component in the following order.
|
||||
// * empty (in this case we return an empty string)
|
||||
// * either C: or {//,\\}net.
|
||||
// * {/,\}
|
||||
// * {file,directory}name
|
||||
|
||||
if (path.empty())
|
||||
return path;
|
||||
|
||||
#ifdef _WIN32
|
||||
// C:
|
||||
if (path.size() >= 2 && std::isalpha(static_cast<unsigned char>(path[0])) &&
|
||||
path[1] == ':')
|
||||
return path.substr(0, 2);
|
||||
#endif
|
||||
|
||||
// //net
|
||||
if ((path.size() > 2) &&
|
||||
is_separator(path[0]) &&
|
||||
path[0] == path[1] &&
|
||||
!is_separator(path[2])) {
|
||||
// Find the next directory separator.
|
||||
size_t end = path.find_first_of(separators, 2);
|
||||
return path.substr(0, end);
|
||||
}
|
||||
|
||||
// {/,\}
|
||||
if (is_separator(path[0]))
|
||||
return path.substr(0, 1);
|
||||
|
||||
// * {file,directory}name
|
||||
size_t end = path.find_first_of(separators);
|
||||
return path.substr(0, end);
|
||||
}
|
||||
|
||||
size_t filename_pos(StringRef str) {
|
||||
if (str.size() == 2 &&
|
||||
is_separator(str[0]) &&
|
||||
str[0] == str[1])
|
||||
return 0;
|
||||
|
||||
if (str.size() > 0 && is_separator(str[str.size() - 1]))
|
||||
return str.size() - 1;
|
||||
|
||||
size_t pos = str.find_last_of(separators, str.size() - 1);
|
||||
|
||||
#ifdef _WIN32
|
||||
if (pos == StringRef::npos)
|
||||
pos = str.find_last_of(':', str.size() - 2);
|
||||
#endif
|
||||
|
||||
if (pos == StringRef::npos ||
|
||||
(pos == 1 && is_separator(str[0])))
|
||||
return 0;
|
||||
|
||||
return pos + 1;
|
||||
}
|
||||
|
||||
size_t root_dir_start(StringRef str) {
|
||||
// case "c:/"
|
||||
#ifdef _WIN32
|
||||
if (str.size() > 2 &&
|
||||
str[1] == ':' &&
|
||||
is_separator(str[2]))
|
||||
return 2;
|
||||
#endif
|
||||
|
||||
// case "//"
|
||||
if (str.size() == 2 &&
|
||||
is_separator(str[0]) &&
|
||||
str[0] == str[1])
|
||||
return StringRef::npos;
|
||||
|
||||
// case "//net"
|
||||
if (str.size() > 3 &&
|
||||
is_separator(str[0]) &&
|
||||
str[0] == str[1] &&
|
||||
!is_separator(str[2])) {
|
||||
return str.find_first_of(separators, 2);
|
||||
}
|
||||
|
||||
// case "/"
|
||||
if (str.size() > 0 && is_separator(str[0]))
|
||||
return 0;
|
||||
|
||||
return StringRef::npos;
|
||||
}
|
||||
|
||||
size_t parent_path_end(StringRef path) {
|
||||
size_t end_pos = filename_pos(path);
|
||||
|
||||
bool filename_was_sep = path.size() > 0 && is_separator(path[end_pos]);
|
||||
|
||||
// Skip separators except for root dir.
|
||||
size_t root_dir_pos = root_dir_start(path.substr(0, end_pos));
|
||||
|
||||
while(end_pos > 0 &&
|
||||
(end_pos - 1) != root_dir_pos &&
|
||||
is_separator(path[end_pos - 1]))
|
||||
--end_pos;
|
||||
|
||||
if (end_pos == 1 && root_dir_pos == 0 && filename_was_sep)
|
||||
return StringRef::npos;
|
||||
|
||||
return end_pos;
|
||||
}
|
||||
} // end unnamed namespace
|
||||
|
||||
namespace llvm {
|
||||
namespace sys {
|
||||
namespace path {
|
||||
|
||||
const_iterator begin(StringRef path) {
|
||||
const_iterator i;
|
||||
i.Path = path;
|
||||
i.Component = find_first_component(path);
|
||||
i.Position = 0;
|
||||
return i;
|
||||
}
|
||||
|
||||
const_iterator end(StringRef path) {
|
||||
const_iterator i;
|
||||
i.Path = path;
|
||||
i.Position = path.size();
|
||||
return i;
|
||||
}
|
||||
|
||||
const_iterator &const_iterator::operator++() {
|
||||
assert(Position < Path.size() && "Tried to increment past end!");
|
||||
|
||||
// Increment Position to past the current component
|
||||
Position += Component.size();
|
||||
|
||||
// Check for end.
|
||||
if (Position == Path.size()) {
|
||||
Component = StringRef();
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Both POSIX and Windows treat paths that begin with exactly two separators
|
||||
// specially.
|
||||
bool was_net = Component.size() > 2 &&
|
||||
is_separator(Component[0]) &&
|
||||
Component[1] == Component[0] &&
|
||||
!is_separator(Component[2]);
|
||||
|
||||
// Handle separators.
|
||||
if (is_separator(Path[Position])) {
|
||||
// Root dir.
|
||||
if (was_net
|
||||
#ifdef _WIN32
|
||||
// c:/
|
||||
|| Component.endswith(":")
|
||||
#endif
|
||||
) {
|
||||
Component = Path.substr(Position, 1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Skip extra separators.
|
||||
while (Position != Path.size() &&
|
||||
is_separator(Path[Position])) {
|
||||
++Position;
|
||||
}
|
||||
|
||||
// Treat trailing '/' as a '.'.
|
||||
if (Position == Path.size()) {
|
||||
--Position;
|
||||
Component = ".";
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
|
||||
// Find next component.
|
||||
size_t end_pos = Path.find_first_of(separators, Position);
|
||||
Component = Path.slice(Position, end_pos);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool const_iterator::operator==(const const_iterator &RHS) const {
|
||||
return Path.begin() == RHS.Path.begin() && Position == RHS.Position;
|
||||
}
|
||||
|
||||
ptrdiff_t const_iterator::operator-(const const_iterator &RHS) const {
|
||||
return Position - RHS.Position;
|
||||
}
|
||||
|
||||
reverse_iterator rbegin(StringRef Path) {
|
||||
reverse_iterator I;
|
||||
I.Path = Path;
|
||||
I.Position = Path.size();
|
||||
return ++I;
|
||||
}
|
||||
|
||||
reverse_iterator rend(StringRef Path) {
|
||||
reverse_iterator I;
|
||||
I.Path = Path;
|
||||
I.Component = Path.substr(0, 0);
|
||||
I.Position = 0;
|
||||
return I;
|
||||
}
|
||||
|
||||
reverse_iterator &reverse_iterator::operator++() {
|
||||
// If we're at the end and the previous char was a '/', return '.' unless
|
||||
// we are the root path.
|
||||
size_t root_dir_pos = root_dir_start(Path);
|
||||
if (Position == Path.size() &&
|
||||
Path.size() > root_dir_pos + 1 &&
|
||||
is_separator(Path[Position - 1])) {
|
||||
--Position;
|
||||
Component = ".";
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Skip separators unless it's the root directory.
|
||||
size_t end_pos = Position;
|
||||
|
||||
while(end_pos > 0 &&
|
||||
(end_pos - 1) != root_dir_pos &&
|
||||
is_separator(Path[end_pos - 1]))
|
||||
--end_pos;
|
||||
|
||||
// Find next separator.
|
||||
size_t start_pos = filename_pos(Path.substr(0, end_pos));
|
||||
Component = Path.slice(start_pos, end_pos);
|
||||
Position = start_pos;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool reverse_iterator::operator==(const reverse_iterator &RHS) const {
|
||||
return Path.begin() == RHS.Path.begin() && Component == RHS.Component &&
|
||||
Position == RHS.Position;
|
||||
}
|
||||
|
||||
ptrdiff_t reverse_iterator::operator-(const reverse_iterator &RHS) const {
|
||||
return Position - RHS.Position;
|
||||
}
|
||||
|
||||
StringRef root_path(StringRef path) {
|
||||
const_iterator b = begin(path),
|
||||
pos = b,
|
||||
e = end(path);
|
||||
if (b != e) {
|
||||
bool has_net = b->size() > 2 && is_separator((*b)[0]) && (*b)[1] == (*b)[0];
|
||||
bool has_drive =
|
||||
#ifdef _WIN32
|
||||
b->endswith(":");
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
|
||||
if (has_net || has_drive) {
|
||||
if ((++pos != e) && is_separator((*pos)[0])) {
|
||||
// {C:/,//net/}, so get the first two components.
|
||||
return path.substr(0, b->size() + pos->size());
|
||||
} else {
|
||||
// just {C:,//net}, return the first component.
|
||||
return *b;
|
||||
}
|
||||
}
|
||||
|
||||
// POSIX style root directory.
|
||||
if (is_separator((*b)[0])) {
|
||||
return *b;
|
||||
}
|
||||
}
|
||||
|
||||
return StringRef();
|
||||
}
|
||||
|
||||
StringRef root_name(StringRef path) {
|
||||
const_iterator b = begin(path),
|
||||
e = end(path);
|
||||
if (b != e) {
|
||||
bool has_net = b->size() > 2 && is_separator((*b)[0]) && (*b)[1] == (*b)[0];
|
||||
bool has_drive =
|
||||
#ifdef _WIN32
|
||||
b->endswith(":");
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
|
||||
if (has_net || has_drive) {
|
||||
// just {C:,//net}, return the first component.
|
||||
return *b;
|
||||
}
|
||||
}
|
||||
|
||||
// No path or no name.
|
||||
return StringRef();
|
||||
}
|
||||
|
||||
StringRef root_directory(StringRef path) {
|
||||
const_iterator b = begin(path),
|
||||
pos = b,
|
||||
e = end(path);
|
||||
if (b != e) {
|
||||
bool has_net = b->size() > 2 && is_separator((*b)[0]) && (*b)[1] == (*b)[0];
|
||||
bool has_drive =
|
||||
#ifdef _WIN32
|
||||
b->endswith(":");
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
|
||||
if ((has_net || has_drive) &&
|
||||
// {C:,//net}, skip to the next component.
|
||||
(++pos != e) && is_separator((*pos)[0])) {
|
||||
return *pos;
|
||||
}
|
||||
|
||||
// POSIX style root directory.
|
||||
if (!has_net && is_separator((*b)[0])) {
|
||||
return *b;
|
||||
}
|
||||
}
|
||||
|
||||
// No path or no root.
|
||||
return StringRef();
|
||||
}
|
||||
|
||||
StringRef relative_path(StringRef path) {
|
||||
StringRef root = root_path(path);
|
||||
return path.substr(root.size());
|
||||
}
|
||||
|
||||
void append(SmallVectorImpl<char> &path, const Twine &a,
|
||||
const Twine &b,
|
||||
const Twine &c,
|
||||
const Twine &d) {
|
||||
SmallString<32> a_storage;
|
||||
SmallString<32> b_storage;
|
||||
SmallString<32> c_storage;
|
||||
SmallString<32> d_storage;
|
||||
|
||||
SmallVector<StringRef, 4> components;
|
||||
if (!a.isTriviallyEmpty()) components.push_back(a.toStringRef(a_storage));
|
||||
if (!b.isTriviallyEmpty()) components.push_back(b.toStringRef(b_storage));
|
||||
if (!c.isTriviallyEmpty()) components.push_back(c.toStringRef(c_storage));
|
||||
if (!d.isTriviallyEmpty()) components.push_back(d.toStringRef(d_storage));
|
||||
|
||||
for (auto &component : components) {
|
||||
bool path_has_sep = !path.empty() && is_separator(path[path.size() - 1]);
|
||||
bool component_has_sep = !component.empty() && is_separator(component[0]);
|
||||
bool is_root_name = has_root_name(component);
|
||||
|
||||
if (path_has_sep) {
|
||||
// Strip separators from beginning of component.
|
||||
size_t loc = component.find_first_not_of(separators);
|
||||
StringRef c = component.substr(loc);
|
||||
|
||||
// Append it.
|
||||
path.append(c.begin(), c.end());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!component_has_sep && !(path.empty() || is_root_name)) {
|
||||
// Add a separator.
|
||||
path.push_back(preferred_separator);
|
||||
}
|
||||
|
||||
path.append(component.begin(), component.end());
|
||||
}
|
||||
}
|
||||
|
||||
void append(SmallVectorImpl<char> &path,
|
||||
const_iterator begin, const_iterator end) {
|
||||
for (; begin != end; ++begin)
|
||||
path::append(path, *begin);
|
||||
}
|
||||
|
||||
StringRef parent_path(StringRef path) {
|
||||
size_t end_pos = parent_path_end(path);
|
||||
if (end_pos == StringRef::npos)
|
||||
return StringRef();
|
||||
else
|
||||
return path.substr(0, end_pos);
|
||||
}
|
||||
|
||||
void remove_filename(SmallVectorImpl<char> &path) {
|
||||
size_t end_pos = parent_path_end(StringRef(path.begin(), path.size()));
|
||||
if (end_pos != StringRef::npos)
|
||||
path.set_size(end_pos);
|
||||
}
|
||||
|
||||
void replace_extension(SmallVectorImpl<char> &path, const Twine &extension) {
|
||||
StringRef p(path.begin(), path.size());
|
||||
SmallString<32> ext_storage;
|
||||
StringRef ext = extension.toStringRef(ext_storage);
|
||||
|
||||
// Erase existing extension.
|
||||
size_t pos = p.find_last_of('.');
|
||||
if (pos != StringRef::npos && pos >= filename_pos(p))
|
||||
path.set_size(pos);
|
||||
|
||||
// Append '.' if needed.
|
||||
if (ext.size() > 0 && ext[0] != '.')
|
||||
path.push_back('.');
|
||||
|
||||
// Append extension.
|
||||
path.append(ext.begin(), ext.end());
|
||||
}
|
||||
|
||||
void replace_path_prefix(SmallVectorImpl<char> &Path,
|
||||
const StringRef &OldPrefix,
|
||||
const StringRef &NewPrefix) {
|
||||
if (OldPrefix.empty() && NewPrefix.empty())
|
||||
return;
|
||||
|
||||
StringRef OrigPath(Path.begin(), Path.size());
|
||||
if (!OrigPath.startswith(OldPrefix))
|
||||
return;
|
||||
|
||||
// If prefixes have the same size we can simply copy the new one over.
|
||||
if (OldPrefix.size() == NewPrefix.size()) {
|
||||
std::copy(NewPrefix.begin(), NewPrefix.end(), Path.begin());
|
||||
return;
|
||||
}
|
||||
|
||||
StringRef RelPath = OrigPath.substr(OldPrefix.size());
|
||||
SmallString<256> NewPath;
|
||||
path::append(NewPath, NewPrefix);
|
||||
path::append(NewPath, RelPath);
|
||||
Path.swap(NewPath);
|
||||
}
|
||||
|
||||
void native(const Twine &path, SmallVectorImpl<char> &result) {
|
||||
assert((!path.isSingleStringRef() ||
|
||||
path.getSingleStringRef().data() != result.data()) &&
|
||||
"path and result are not allowed to overlap!");
|
||||
// Clear result.
|
||||
result.clear();
|
||||
path.toVector(result);
|
||||
native(result);
|
||||
}
|
||||
|
||||
void native(SmallVectorImpl<char> &Path) {
|
||||
#ifdef _WIN32
|
||||
std::replace(Path.begin(), Path.end(), '/', '\\');
|
||||
#else
|
||||
for (auto PI = Path.begin(), PE = Path.end(); PI < PE; ++PI) {
|
||||
if (*PI == '\\') {
|
||||
auto PN = PI + 1;
|
||||
if (PN < PE && *PN == '\\')
|
||||
++PI; // increment once, the for loop will move over the escaped slash
|
||||
else
|
||||
*PI = '/';
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
StringRef filename(StringRef path) {
|
||||
return *rbegin(path);
|
||||
}
|
||||
|
||||
StringRef stem(StringRef path) {
|
||||
StringRef fname = filename(path);
|
||||
size_t pos = fname.find_last_of('.');
|
||||
if (pos == StringRef::npos)
|
||||
return fname;
|
||||
else
|
||||
if ((fname.size() == 1 && fname == ".") ||
|
||||
(fname.size() == 2 && fname == ".."))
|
||||
return fname;
|
||||
else
|
||||
return fname.substr(0, pos);
|
||||
}
|
||||
|
||||
StringRef extension(StringRef path) {
|
||||
StringRef fname = filename(path);
|
||||
size_t pos = fname.find_last_of('.');
|
||||
if (pos == StringRef::npos)
|
||||
return StringRef();
|
||||
else
|
||||
if ((fname.size() == 1 && fname == ".") ||
|
||||
(fname.size() == 2 && fname == ".."))
|
||||
return StringRef();
|
||||
else
|
||||
return fname.substr(pos);
|
||||
}
|
||||
|
||||
bool is_separator(char value) {
|
||||
switch(value) {
|
||||
#ifdef _WIN32
|
||||
case '\\': // fall through
|
||||
#endif
|
||||
case '/': return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
static const char preferred_separator_string[] = { preferred_separator, '\0' };
|
||||
|
||||
StringRef get_separator() {
|
||||
return preferred_separator_string;
|
||||
}
|
||||
|
||||
bool has_root_name(const Twine &path) {
|
||||
SmallString<128> path_storage;
|
||||
StringRef p = path.toStringRef(path_storage);
|
||||
|
||||
return !root_name(p).empty();
|
||||
}
|
||||
|
||||
bool has_root_directory(const Twine &path) {
|
||||
SmallString<128> path_storage;
|
||||
StringRef p = path.toStringRef(path_storage);
|
||||
|
||||
return !root_directory(p).empty();
|
||||
}
|
||||
|
||||
bool has_root_path(const Twine &path) {
|
||||
SmallString<128> path_storage;
|
||||
StringRef p = path.toStringRef(path_storage);
|
||||
|
||||
return !root_path(p).empty();
|
||||
}
|
||||
|
||||
bool has_relative_path(const Twine &path) {
|
||||
SmallString<128> path_storage;
|
||||
StringRef p = path.toStringRef(path_storage);
|
||||
|
||||
return !relative_path(p).empty();
|
||||
}
|
||||
|
||||
bool has_filename(const Twine &path) {
|
||||
SmallString<128> path_storage;
|
||||
StringRef p = path.toStringRef(path_storage);
|
||||
|
||||
return !filename(p).empty();
|
||||
}
|
||||
|
||||
bool has_parent_path(const Twine &path) {
|
||||
SmallString<128> path_storage;
|
||||
StringRef p = path.toStringRef(path_storage);
|
||||
|
||||
return !parent_path(p).empty();
|
||||
}
|
||||
|
||||
bool has_stem(const Twine &path) {
|
||||
SmallString<128> path_storage;
|
||||
StringRef p = path.toStringRef(path_storage);
|
||||
|
||||
return !stem(p).empty();
|
||||
}
|
||||
|
||||
bool has_extension(const Twine &path) {
|
||||
SmallString<128> path_storage;
|
||||
StringRef p = path.toStringRef(path_storage);
|
||||
|
||||
return !extension(p).empty();
|
||||
}
|
||||
|
||||
bool is_absolute(const Twine &path) {
|
||||
SmallString<128> path_storage;
|
||||
StringRef p = path.toStringRef(path_storage);
|
||||
|
||||
bool rootDir = has_root_directory(p),
|
||||
#ifdef _WIN32
|
||||
rootName = has_root_name(p);
|
||||
#else
|
||||
rootName = true;
|
||||
#endif
|
||||
|
||||
return rootDir && rootName;
|
||||
}
|
||||
|
||||
bool is_relative(const Twine &path) { return !is_absolute(path); }
|
||||
|
||||
StringRef remove_leading_dotslash(StringRef Path) {
|
||||
// Remove leading "./" (or ".//" or "././" etc.)
|
||||
while (Path.size() > 2 && Path[0] == '.' && is_separator(Path[1])) {
|
||||
Path = Path.substr(2);
|
||||
while (Path.size() > 0 && is_separator(Path[0]))
|
||||
Path = Path.substr(1);
|
||||
}
|
||||
return Path;
|
||||
}
|
||||
|
||||
static SmallString<256> remove_dots(StringRef path, bool remove_dot_dot) {
|
||||
SmallVector<StringRef, 16> components;
|
||||
|
||||
// Skip the root path, then look for traversal in the components.
|
||||
StringRef rel = path::relative_path(path);
|
||||
for (StringRef C : llvm::make_range(path::begin(rel), path::end(rel))) {
|
||||
if (C == ".")
|
||||
continue;
|
||||
if (remove_dot_dot) {
|
||||
if (C == "..") {
|
||||
if (!components.empty())
|
||||
components.pop_back();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
components.push_back(C);
|
||||
}
|
||||
|
||||
SmallString<256> buffer = path::root_path(path);
|
||||
for (StringRef C : components)
|
||||
path::append(buffer, C);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool remove_dots(SmallVectorImpl<char> &path, bool remove_dot_dot) {
|
||||
StringRef p(path.data(), path.size());
|
||||
|
||||
SmallString<256> result = remove_dots(p, remove_dot_dot);
|
||||
if (result == path)
|
||||
return false;
|
||||
|
||||
path.swap(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // end namespace path
|
||||
|
||||
namespace fs {
|
||||
|
||||
std::error_code getUniqueID(const Twine Path, UniqueID &Result) {
|
||||
file_status Status;
|
||||
std::error_code EC = status(Path, Status);
|
||||
if (EC)
|
||||
return EC;
|
||||
Result = Status.getUniqueID();
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
static std::error_code make_absolute(const Twine ¤t_directory,
|
||||
SmallVectorImpl<char> &path,
|
||||
bool use_current_directory) {
|
||||
StringRef p(path.data(), path.size());
|
||||
|
||||
bool rootDirectory = path::has_root_directory(p),
|
||||
#ifdef _WIN32
|
||||
rootName = path::has_root_name(p);
|
||||
#else
|
||||
rootName = true;
|
||||
#endif
|
||||
|
||||
// Already absolute.
|
||||
if (rootName && rootDirectory)
|
||||
return std::error_code();
|
||||
|
||||
// All of the following conditions will need the current directory.
|
||||
SmallString<128> current_dir;
|
||||
if (use_current_directory)
|
||||
current_directory.toVector(current_dir);
|
||||
else if (std::error_code ec = current_path(current_dir))
|
||||
return ec;
|
||||
|
||||
// Relative path. Prepend the current directory.
|
||||
if (!rootName && !rootDirectory) {
|
||||
// Append path to the current directory.
|
||||
path::append(current_dir, p);
|
||||
// Set path to the result.
|
||||
path.swap(current_dir);
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
if (!rootName && rootDirectory) {
|
||||
StringRef cdrn = path::root_name(current_dir);
|
||||
SmallString<128> curDirRootName(cdrn.begin(), cdrn.end());
|
||||
path::append(curDirRootName, p);
|
||||
// Set path to the result.
|
||||
path.swap(curDirRootName);
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
if (rootName && !rootDirectory) {
|
||||
StringRef pRootName = path::root_name(p);
|
||||
StringRef bRootDirectory = path::root_directory(current_dir);
|
||||
StringRef bRelativePath = path::relative_path(current_dir);
|
||||
StringRef pRelativePath = path::relative_path(p);
|
||||
|
||||
SmallString<128> res;
|
||||
path::append(res, pRootName, bRootDirectory, bRelativePath, pRelativePath);
|
||||
path.swap(res);
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
assert(false && "All rootName and rootDirectory combinations should have "
|
||||
"occurred above!");
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
std::error_code make_absolute(const Twine ¤t_directory,
|
||||
SmallVectorImpl<char> &path) {
|
||||
return make_absolute(current_directory, path, true);
|
||||
}
|
||||
|
||||
std::error_code make_absolute(SmallVectorImpl<char> &path) {
|
||||
return make_absolute(Twine(), path, false);
|
||||
}
|
||||
|
||||
bool exists(file_status status) {
|
||||
return status_known(status) && status.type() != file_type::file_not_found;
|
||||
}
|
||||
|
||||
bool status_known(file_status s) {
|
||||
return s.type() != file_type::status_error;
|
||||
}
|
||||
|
||||
bool is_directory(file_status status) {
|
||||
return status.type() == file_type::directory_file;
|
||||
}
|
||||
|
||||
std::error_code is_directory(const Twine &path, bool &result) {
|
||||
file_status st;
|
||||
if (std::error_code ec = status(path, st))
|
||||
return ec;
|
||||
result = is_directory(st);
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
bool is_regular_file(file_status status) {
|
||||
return status.type() == file_type::regular_file;
|
||||
}
|
||||
|
||||
std::error_code is_regular_file(const Twine &path, bool &result) {
|
||||
file_status st;
|
||||
if (std::error_code ec = status(path, st))
|
||||
return ec;
|
||||
result = is_regular_file(st);
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
bool is_other(file_status status) {
|
||||
return exists(status) &&
|
||||
!is_regular_file(status) &&
|
||||
!is_directory(status);
|
||||
}
|
||||
|
||||
std::error_code is_other(const Twine &Path, bool &Result) {
|
||||
file_status FileStatus;
|
||||
if (std::error_code EC = status(Path, FileStatus))
|
||||
return EC;
|
||||
Result = is_other(FileStatus);
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
void directory_entry::replace_filename(const Twine &filename, file_status st) {
|
||||
SmallString<128> path = path::parent_path(Path);
|
||||
path::append(path, filename);
|
||||
Path = path.str();
|
||||
Status = st;
|
||||
}
|
||||
|
||||
std::error_code directory_entry::status(file_status &result) const {
|
||||
return fs::status(Path, result);
|
||||
}
|
||||
|
||||
} // end namespace fs
|
||||
} // end namespace sys
|
||||
} // end namespace llvm
|
||||
|
||||
// Include the truly platform-specific parts.
|
||||
#ifdef _WIN32
|
||||
#include "Windows/Path.inc"
|
||||
#else
|
||||
#include "Unix/Path.inc"
|
||||
#endif
|
||||
|
||||
namespace llvm {
|
||||
namespace sys {
|
||||
namespace path {
|
||||
|
||||
bool user_cache_directory(SmallVectorImpl<char> &Result, const Twine &Path1,
|
||||
const Twine &Path2, const Twine &Path3) {
|
||||
if (getUserCacheDir(Result)) {
|
||||
append(Result, Path1, Path2, Path3);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // end namespace path
|
||||
} // end namsspace sys
|
||||
} // end namespace llvm
|
||||
295
wpiutil/src/main/native/cpp/llvm/SmallPtrSet.cpp
Normal file
295
wpiutil/src/main/native/cpp/llvm/SmallPtrSet.cpp
Normal file
@@ -0,0 +1,295 @@
|
||||
//===- llvm/ADT/SmallPtrSet.cpp - 'Normally small' pointer set ------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements the SmallPtrSet class. See SmallPtrSet.h for an
|
||||
// overview of the algorithm.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/SmallPtrSet.h"
|
||||
#include "llvm/DenseMapInfo.h"
|
||||
#include "llvm/MathExtras.h"
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
void SmallPtrSetImplBase::shrink_and_clear() {
|
||||
assert(!isSmall() && "Can't shrink a small set!");
|
||||
free(CurArray);
|
||||
|
||||
// Reduce the number of buckets.
|
||||
unsigned Size = size();
|
||||
CurArraySize = Size > 16 ? 1 << (Log2_32_Ceil(Size) + 1) : 32;
|
||||
NumNonEmpty = NumTombstones = 0;
|
||||
|
||||
// Install the new array. Clear all the buckets to empty.
|
||||
CurArray = (const void**)malloc(sizeof(void*) * CurArraySize);
|
||||
assert(CurArray && "Failed to allocate memory?");
|
||||
memset(CurArray, -1, CurArraySize*sizeof(void*));
|
||||
}
|
||||
|
||||
std::pair<const void *const *, bool>
|
||||
SmallPtrSetImplBase::insert_imp_big(const void *Ptr) {
|
||||
if (LLVM_UNLIKELY(size() * 4 >= CurArraySize * 3)) {
|
||||
// If more than 3/4 of the array is full, grow.
|
||||
Grow(CurArraySize < 64 ? 128 : CurArraySize * 2);
|
||||
} else if (LLVM_UNLIKELY(CurArraySize - NumNonEmpty < CurArraySize / 8)) {
|
||||
// If fewer of 1/8 of the array is empty (meaning that many are filled with
|
||||
// tombstones), rehash.
|
||||
Grow(CurArraySize);
|
||||
}
|
||||
|
||||
// Okay, we know we have space. Find a hash bucket.
|
||||
const void **Bucket = const_cast<const void**>(FindBucketFor(Ptr));
|
||||
if (*Bucket == Ptr)
|
||||
return std::make_pair(Bucket, false); // Already inserted, good.
|
||||
|
||||
// Otherwise, insert it!
|
||||
if (*Bucket == getTombstoneMarker())
|
||||
--NumTombstones;
|
||||
else
|
||||
++NumNonEmpty; // Track density.
|
||||
*Bucket = Ptr;
|
||||
return std::make_pair(Bucket, true);
|
||||
}
|
||||
|
||||
bool SmallPtrSetImplBase::erase_imp(const void * Ptr) {
|
||||
if (isSmall()) {
|
||||
// Check to see if it is in the set.
|
||||
for (const void **APtr = CurArray, **E = CurArray + NumNonEmpty; APtr != E;
|
||||
++APtr)
|
||||
if (*APtr == Ptr) {
|
||||
// If it is in the set, replace this element.
|
||||
*APtr = getTombstoneMarker();
|
||||
++NumTombstones;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Okay, we know we have space. Find a hash bucket.
|
||||
void **Bucket = const_cast<void**>(FindBucketFor(Ptr));
|
||||
if (*Bucket != Ptr) return false; // Not in the set?
|
||||
|
||||
// Set this as a tombstone.
|
||||
*Bucket = getTombstoneMarker();
|
||||
++NumTombstones;
|
||||
return true;
|
||||
}
|
||||
|
||||
const void * const *SmallPtrSetImplBase::FindBucketFor(const void *Ptr) const {
|
||||
unsigned Bucket = DenseMapInfo<void *>::getHashValue(Ptr) & (CurArraySize-1);
|
||||
unsigned ArraySize = CurArraySize;
|
||||
unsigned ProbeAmt = 1;
|
||||
const void *const *Array = CurArray;
|
||||
const void *const *Tombstone = nullptr;
|
||||
while (1) {
|
||||
// If we found an empty bucket, the pointer doesn't exist in the set.
|
||||
// Return a tombstone if we've seen one so far, or the empty bucket if
|
||||
// not.
|
||||
if (LLVM_LIKELY(Array[Bucket] == getEmptyMarker()))
|
||||
return Tombstone ? Tombstone : Array+Bucket;
|
||||
|
||||
// Found Ptr's bucket?
|
||||
if (LLVM_LIKELY(Array[Bucket] == Ptr))
|
||||
return Array+Bucket;
|
||||
|
||||
// If this is a tombstone, remember it. If Ptr ends up not in the set, we
|
||||
// prefer to return it than something that would require more probing.
|
||||
if (Array[Bucket] == getTombstoneMarker() && !Tombstone)
|
||||
Tombstone = Array+Bucket; // Remember the first tombstone found.
|
||||
|
||||
// It's a hash collision or a tombstone. Reprobe.
|
||||
Bucket = (Bucket + ProbeAmt++) & (ArraySize-1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Grow - Allocate a larger backing store for the buckets and move it over.
|
||||
///
|
||||
void SmallPtrSetImplBase::Grow(unsigned NewSize) {
|
||||
const void **OldBuckets = CurArray;
|
||||
const void **OldEnd = EndPointer();
|
||||
bool WasSmall = isSmall();
|
||||
|
||||
// Install the new array. Clear all the buckets to empty.
|
||||
CurArray = (const void**)malloc(sizeof(void*) * NewSize);
|
||||
assert(CurArray && "Failed to allocate memory?");
|
||||
CurArraySize = NewSize;
|
||||
memset(CurArray, -1, NewSize*sizeof(void*));
|
||||
|
||||
// Copy over all valid entries.
|
||||
for (const void **BucketPtr = OldBuckets; BucketPtr != OldEnd; ++BucketPtr) {
|
||||
// Copy over the element if it is valid.
|
||||
const void *Elt = *BucketPtr;
|
||||
if (Elt != getTombstoneMarker() && Elt != getEmptyMarker())
|
||||
*const_cast<void**>(FindBucketFor(Elt)) = const_cast<void*>(Elt);
|
||||
}
|
||||
|
||||
if (!WasSmall)
|
||||
free(OldBuckets);
|
||||
NumNonEmpty -= NumTombstones;
|
||||
NumTombstones = 0;
|
||||
}
|
||||
|
||||
SmallPtrSetImplBase::SmallPtrSetImplBase(const void **SmallStorage,
|
||||
const SmallPtrSetImplBase &that) {
|
||||
SmallArray = SmallStorage;
|
||||
|
||||
// If we're becoming small, prepare to insert into our stack space
|
||||
if (that.isSmall()) {
|
||||
CurArray = SmallArray;
|
||||
// Otherwise, allocate new heap space (unless we were the same size)
|
||||
} else {
|
||||
CurArray = (const void**)malloc(sizeof(void*) * that.CurArraySize);
|
||||
assert(CurArray && "Failed to allocate memory?");
|
||||
}
|
||||
|
||||
// Copy over the that array.
|
||||
CopyHelper(that);
|
||||
}
|
||||
|
||||
SmallPtrSetImplBase::SmallPtrSetImplBase(const void **SmallStorage,
|
||||
unsigned SmallSize,
|
||||
SmallPtrSetImplBase &&that) {
|
||||
SmallArray = SmallStorage;
|
||||
MoveHelper(SmallSize, std::move(that));
|
||||
}
|
||||
|
||||
void SmallPtrSetImplBase::CopyFrom(const SmallPtrSetImplBase &RHS) {
|
||||
assert(&RHS != this && "Self-copy should be handled by the caller.");
|
||||
|
||||
if (isSmall() && RHS.isSmall())
|
||||
assert(CurArraySize == RHS.CurArraySize &&
|
||||
"Cannot assign sets with different small sizes");
|
||||
|
||||
// If we're becoming small, prepare to insert into our stack space
|
||||
if (RHS.isSmall()) {
|
||||
if (!isSmall())
|
||||
free(CurArray);
|
||||
CurArray = SmallArray;
|
||||
// Otherwise, allocate new heap space (unless we were the same size)
|
||||
} else if (CurArraySize != RHS.CurArraySize) {
|
||||
if (isSmall())
|
||||
CurArray = (const void**)malloc(sizeof(void*) * RHS.CurArraySize);
|
||||
else {
|
||||
const void **T = (const void**)realloc(CurArray,
|
||||
sizeof(void*) * RHS.CurArraySize);
|
||||
if (!T)
|
||||
free(CurArray);
|
||||
CurArray = T;
|
||||
}
|
||||
assert(CurArray && "Failed to allocate memory?");
|
||||
}
|
||||
|
||||
CopyHelper(RHS);
|
||||
}
|
||||
|
||||
void SmallPtrSetImplBase::CopyHelper(const SmallPtrSetImplBase &RHS) {
|
||||
// Copy over the new array size
|
||||
CurArraySize = RHS.CurArraySize;
|
||||
|
||||
// Copy over the contents from the other set
|
||||
std::copy(RHS.CurArray, RHS.EndPointer(), CurArray);
|
||||
|
||||
NumNonEmpty = RHS.NumNonEmpty;
|
||||
NumTombstones = RHS.NumTombstones;
|
||||
}
|
||||
|
||||
void SmallPtrSetImplBase::MoveFrom(unsigned SmallSize,
|
||||
SmallPtrSetImplBase &&RHS) {
|
||||
if (!isSmall())
|
||||
free(CurArray);
|
||||
MoveHelper(SmallSize, std::move(RHS));
|
||||
}
|
||||
|
||||
void SmallPtrSetImplBase::MoveHelper(unsigned SmallSize,
|
||||
SmallPtrSetImplBase &&RHS) {
|
||||
assert(&RHS != this && "Self-move should be handled by the caller.");
|
||||
|
||||
if (RHS.isSmall()) {
|
||||
// Copy a small RHS rather than moving.
|
||||
CurArray = SmallArray;
|
||||
std::copy(RHS.CurArray, RHS.CurArray + RHS.NumNonEmpty, CurArray);
|
||||
} else {
|
||||
CurArray = RHS.CurArray;
|
||||
RHS.CurArray = RHS.SmallArray;
|
||||
}
|
||||
|
||||
// Copy the rest of the trivial members.
|
||||
CurArraySize = RHS.CurArraySize;
|
||||
NumNonEmpty = RHS.NumNonEmpty;
|
||||
NumTombstones = RHS.NumTombstones;
|
||||
|
||||
// Make the RHS small and empty.
|
||||
RHS.CurArraySize = SmallSize;
|
||||
assert(RHS.CurArray == RHS.SmallArray);
|
||||
RHS.NumNonEmpty = 0;
|
||||
RHS.NumTombstones = 0;
|
||||
}
|
||||
|
||||
void SmallPtrSetImplBase::swap(SmallPtrSetImplBase &RHS) {
|
||||
if (this == &RHS) return;
|
||||
|
||||
// We can only avoid copying elements if neither set is small.
|
||||
if (!this->isSmall() && !RHS.isSmall()) {
|
||||
std::swap(this->CurArray, RHS.CurArray);
|
||||
std::swap(this->CurArraySize, RHS.CurArraySize);
|
||||
std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
|
||||
std::swap(this->NumTombstones, RHS.NumTombstones);
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: From here on we assume that both sets have the same small size.
|
||||
|
||||
// If only RHS is small, copy the small elements into LHS and move the pointer
|
||||
// from LHS to RHS.
|
||||
if (!this->isSmall() && RHS.isSmall()) {
|
||||
assert(RHS.CurArray == RHS.SmallArray);
|
||||
std::copy(RHS.CurArray, RHS.CurArray + RHS.NumNonEmpty, this->SmallArray);
|
||||
std::swap(RHS.CurArraySize, this->CurArraySize);
|
||||
std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
|
||||
std::swap(this->NumTombstones, RHS.NumTombstones);
|
||||
RHS.CurArray = this->CurArray;
|
||||
this->CurArray = this->SmallArray;
|
||||
return;
|
||||
}
|
||||
|
||||
// If only LHS is small, copy the small elements into RHS and move the pointer
|
||||
// from RHS to LHS.
|
||||
if (this->isSmall() && !RHS.isSmall()) {
|
||||
assert(this->CurArray == this->SmallArray);
|
||||
std::copy(this->CurArray, this->CurArray + this->NumNonEmpty,
|
||||
RHS.SmallArray);
|
||||
std::swap(RHS.CurArraySize, this->CurArraySize);
|
||||
std::swap(RHS.NumNonEmpty, this->NumNonEmpty);
|
||||
std::swap(RHS.NumTombstones, this->NumTombstones);
|
||||
this->CurArray = RHS.CurArray;
|
||||
RHS.CurArray = RHS.SmallArray;
|
||||
return;
|
||||
}
|
||||
|
||||
// Both a small, just swap the small elements.
|
||||
assert(this->isSmall() && RHS.isSmall());
|
||||
unsigned MinNonEmpty = std::min(this->NumNonEmpty, RHS.NumNonEmpty);
|
||||
std::swap_ranges(this->SmallArray, this->SmallArray + MinNonEmpty,
|
||||
RHS.SmallArray);
|
||||
if (this->NumNonEmpty > MinNonEmpty) {
|
||||
std::copy(this->SmallArray + MinNonEmpty,
|
||||
this->SmallArray + this->NumNonEmpty,
|
||||
RHS.SmallArray + MinNonEmpty);
|
||||
} else {
|
||||
std::copy(RHS.SmallArray + MinNonEmpty, RHS.SmallArray + RHS.NumNonEmpty,
|
||||
this->SmallArray + MinNonEmpty);
|
||||
}
|
||||
assert(this->CurArraySize == RHS.CurArraySize);
|
||||
std::swap(this->NumNonEmpty, RHS.NumNonEmpty);
|
||||
std::swap(this->NumTombstones, RHS.NumTombstones);
|
||||
}
|
||||
41
wpiutil/src/main/native/cpp/llvm/SmallVector.cpp
Normal file
41
wpiutil/src/main/native/cpp/llvm/SmallVector.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
//===- llvm/ADT/SmallVector.cpp - 'Normally small' vectors ----------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements the SmallVector class.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/SmallVector.h"
|
||||
using namespace llvm;
|
||||
|
||||
/// grow_pod - This is an implementation of the grow() method which only works
|
||||
/// on POD-like datatypes and is out of line to reduce code duplication.
|
||||
void SmallVectorBase::grow_pod(void *FirstEl, size_t MinSizeInBytes,
|
||||
size_t TSize) {
|
||||
size_t CurSizeBytes = size_in_bytes();
|
||||
size_t NewCapacityInBytes = 2 * capacity_in_bytes() + TSize; // Always grow.
|
||||
if (NewCapacityInBytes < MinSizeInBytes)
|
||||
NewCapacityInBytes = MinSizeInBytes;
|
||||
|
||||
void *NewElts;
|
||||
if (BeginX == FirstEl) {
|
||||
NewElts = malloc(NewCapacityInBytes);
|
||||
|
||||
// Copy the elements over. No need to run dtors on PODs.
|
||||
memcpy(NewElts, this->BeginX, CurSizeBytes);
|
||||
} else {
|
||||
// If this wasn't grown from the inline copy, grow the allocated space.
|
||||
NewElts = realloc(this->BeginX, NewCapacityInBytes);
|
||||
}
|
||||
assert(NewElts && "Out of memory");
|
||||
|
||||
this->EndX = (char*)NewElts+CurSizeBytes;
|
||||
this->BeginX = NewElts;
|
||||
this->CapacityX = (char*)this->BeginX + NewCapacityInBytes;
|
||||
}
|
||||
58
wpiutil/src/main/native/cpp/llvm/StringExtras.cpp
Normal file
58
wpiutil/src/main/native/cpp/llvm/StringExtras.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
//===-- StringExtras.cpp - Implement the StringExtras header --------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements the StringExtras.h header
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/StringExtras.h"
|
||||
#include "llvm/SmallVector.h"
|
||||
using namespace llvm;
|
||||
|
||||
/// StrInStrNoCase - Portable version of strcasestr. Locates the first
|
||||
/// occurrence of string 's1' in string 's2', ignoring case. Returns
|
||||
/// the offset of s2 in s1 or npos if s2 cannot be found.
|
||||
StringRef::size_type llvm::StrInStrNoCase(StringRef s1, StringRef s2) {
|
||||
size_t N = s2.size(), M = s1.size();
|
||||
if (N > M)
|
||||
return StringRef::npos;
|
||||
for (size_t i = 0, e = M - N + 1; i != e; ++i)
|
||||
if (s1.substr(i, N).equals_lower(s2))
|
||||
return i;
|
||||
return StringRef::npos;
|
||||
}
|
||||
|
||||
/// getToken - This function extracts one token from source, ignoring any
|
||||
/// leading characters that appear in the Delimiters string, and ending the
|
||||
/// token at any of the characters that appear in the Delimiters string. If
|
||||
/// there are no tokens in the source string, an empty string is returned.
|
||||
/// The function returns a pair containing the extracted token and the
|
||||
/// remaining tail string.
|
||||
std::pair<StringRef, StringRef> llvm::getToken(StringRef Source,
|
||||
StringRef Delimiters) {
|
||||
// Figure out where the token starts.
|
||||
StringRef::size_type Start = Source.find_first_not_of(Delimiters);
|
||||
|
||||
// Find the next occurrence of the delimiter.
|
||||
StringRef::size_type End = Source.find_first_of(Delimiters, Start);
|
||||
|
||||
return std::make_pair(Source.slice(Start, End), Source.substr(End));
|
||||
}
|
||||
|
||||
/// SplitString - Split up the specified string according to the specified
|
||||
/// delimiters, appending the result fragments to the output list.
|
||||
void llvm::SplitString(StringRef Source,
|
||||
SmallVectorImpl<StringRef> &OutFragments,
|
||||
StringRef Delimiters) {
|
||||
std::pair<StringRef, StringRef> S = getToken(Source, Delimiters);
|
||||
while (!S.first.empty()) {
|
||||
OutFragments.push_back(S.first);
|
||||
S = getToken(S.second, Delimiters);
|
||||
}
|
||||
}
|
||||
260
wpiutil/src/main/native/cpp/llvm/StringMap.cpp
Normal file
260
wpiutil/src/main/native/cpp/llvm/StringMap.cpp
Normal file
@@ -0,0 +1,260 @@
|
||||
//===--- StringMap.cpp - String Hash table map implementation -------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements the StringMap class.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/StringMap.h"
|
||||
#include "llvm/MathExtras.h"
|
||||
#include "llvm/StringExtras.h"
|
||||
#include "llvm/Compiler.h"
|
||||
#include <cassert>
|
||||
using namespace llvm;
|
||||
|
||||
/// Returns the number of buckets to allocate to ensure that the DenseMap can
|
||||
/// accommodate \p NumEntries without need to grow().
|
||||
static unsigned getMinBucketToReserveForEntries(unsigned NumEntries) {
|
||||
// Ensure that "NumEntries * 4 < NumBuckets * 3"
|
||||
if (NumEntries == 0)
|
||||
return 0;
|
||||
// +1 is required because of the strict equality.
|
||||
// For example if NumEntries is 48, we need to return 401.
|
||||
return NextPowerOf2(NumEntries * 4 / 3 + 1);
|
||||
}
|
||||
|
||||
StringMapImpl::StringMapImpl(unsigned InitSize, unsigned itemSize) {
|
||||
ItemSize = itemSize;
|
||||
|
||||
// If a size is specified, initialize the table with that many buckets.
|
||||
if (InitSize) {
|
||||
// The table will grow when the number of entries reach 3/4 of the number of
|
||||
// buckets. To guarantee that "InitSize" number of entries can be inserted
|
||||
// in the table without growing, we allocate just what is needed here.
|
||||
init(getMinBucketToReserveForEntries(InitSize));
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, initialize it with zero buckets to avoid the allocation.
|
||||
TheTable = nullptr;
|
||||
NumBuckets = 0;
|
||||
NumItems = 0;
|
||||
NumTombstones = 0;
|
||||
}
|
||||
|
||||
void StringMapImpl::init(unsigned InitSize) {
|
||||
assert((InitSize & (InitSize-1)) == 0 &&
|
||||
"Init Size must be a power of 2 or zero!");
|
||||
NumBuckets = InitSize ? InitSize : 16;
|
||||
NumItems = 0;
|
||||
NumTombstones = 0;
|
||||
|
||||
TheTable = (StringMapEntryBase **)calloc(NumBuckets+1,
|
||||
sizeof(StringMapEntryBase **) +
|
||||
sizeof(unsigned));
|
||||
|
||||
// Allocate one extra bucket, set it to look filled so the iterators stop at
|
||||
// end.
|
||||
TheTable[NumBuckets] = (StringMapEntryBase*)2;
|
||||
}
|
||||
|
||||
|
||||
/// LookupBucketFor - Look up the bucket that the specified string should end
|
||||
/// up in. If it already exists as a key in the map, the Item pointer for the
|
||||
/// specified bucket will be non-null. Otherwise, it will be null. In either
|
||||
/// case, the FullHashValue field of the bucket will be set to the hash value
|
||||
/// of the string.
|
||||
unsigned StringMapImpl::LookupBucketFor(StringRef Name) {
|
||||
unsigned HTSize = NumBuckets;
|
||||
if (HTSize == 0) { // Hash table unallocated so far?
|
||||
init(16);
|
||||
HTSize = NumBuckets;
|
||||
}
|
||||
unsigned FullHashValue = HashString(Name);
|
||||
unsigned BucketNo = FullHashValue & (HTSize-1);
|
||||
unsigned *HashTable = (unsigned *)(TheTable + NumBuckets + 1);
|
||||
|
||||
unsigned ProbeAmt = 1;
|
||||
int FirstTombstone = -1;
|
||||
while (1) {
|
||||
StringMapEntryBase *BucketItem = TheTable[BucketNo];
|
||||
// If we found an empty bucket, this key isn't in the table yet, return it.
|
||||
if (LLVM_LIKELY(!BucketItem)) {
|
||||
// If we found a tombstone, we want to reuse the tombstone instead of an
|
||||
// empty bucket. This reduces probing.
|
||||
if (FirstTombstone != -1) {
|
||||
HashTable[FirstTombstone] = FullHashValue;
|
||||
return FirstTombstone;
|
||||
}
|
||||
|
||||
HashTable[BucketNo] = FullHashValue;
|
||||
return BucketNo;
|
||||
}
|
||||
|
||||
if (BucketItem == getTombstoneVal()) {
|
||||
// Skip over tombstones. However, remember the first one we see.
|
||||
if (FirstTombstone == -1) FirstTombstone = BucketNo;
|
||||
} else if (LLVM_LIKELY(HashTable[BucketNo] == FullHashValue)) {
|
||||
// If the full hash value matches, check deeply for a match. The common
|
||||
// case here is that we are only looking at the buckets (for item info
|
||||
// being non-null and for the full hash value) not at the items. This
|
||||
// is important for cache locality.
|
||||
|
||||
// Do the comparison like this because Name isn't necessarily
|
||||
// null-terminated!
|
||||
char *ItemStr = (char*)BucketItem+ItemSize;
|
||||
if (Name == StringRef(ItemStr, BucketItem->getKeyLength())) {
|
||||
// We found a match!
|
||||
return BucketNo;
|
||||
}
|
||||
}
|
||||
|
||||
// Okay, we didn't find the item. Probe to the next bucket.
|
||||
BucketNo = (BucketNo+ProbeAmt) & (HTSize-1);
|
||||
|
||||
// Use quadratic probing, it has fewer clumping artifacts than linear
|
||||
// probing and has good cache behavior in the common case.
|
||||
++ProbeAmt;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// FindKey - Look up the bucket that contains the specified key. If it exists
|
||||
/// in the map, return the bucket number of the key. Otherwise return -1.
|
||||
/// This does not modify the map.
|
||||
int StringMapImpl::FindKey(StringRef Key) const {
|
||||
unsigned HTSize = NumBuckets;
|
||||
if (HTSize == 0) return -1; // Really empty table?
|
||||
unsigned FullHashValue = HashString(Key);
|
||||
unsigned BucketNo = FullHashValue & (HTSize-1);
|
||||
unsigned *HashTable = (unsigned *)(TheTable + NumBuckets + 1);
|
||||
|
||||
unsigned ProbeAmt = 1;
|
||||
while (1) {
|
||||
StringMapEntryBase *BucketItem = TheTable[BucketNo];
|
||||
// If we found an empty bucket, this key isn't in the table yet, return.
|
||||
if (LLVM_LIKELY(!BucketItem))
|
||||
return -1;
|
||||
|
||||
if (BucketItem == getTombstoneVal()) {
|
||||
// Ignore tombstones.
|
||||
} else if (LLVM_LIKELY(HashTable[BucketNo] == FullHashValue)) {
|
||||
// If the full hash value matches, check deeply for a match. The common
|
||||
// case here is that we are only looking at the buckets (for item info
|
||||
// being non-null and for the full hash value) not at the items. This
|
||||
// is important for cache locality.
|
||||
|
||||
// Do the comparison like this because NameStart isn't necessarily
|
||||
// null-terminated!
|
||||
char *ItemStr = (char*)BucketItem+ItemSize;
|
||||
if (Key == StringRef(ItemStr, BucketItem->getKeyLength())) {
|
||||
// We found a match!
|
||||
return BucketNo;
|
||||
}
|
||||
}
|
||||
|
||||
// Okay, we didn't find the item. Probe to the next bucket.
|
||||
BucketNo = (BucketNo+ProbeAmt) & (HTSize-1);
|
||||
|
||||
// Use quadratic probing, it has fewer clumping artifacts than linear
|
||||
// probing and has good cache behavior in the common case.
|
||||
++ProbeAmt;
|
||||
}
|
||||
}
|
||||
|
||||
/// RemoveKey - Remove the specified StringMapEntry from the table, but do not
|
||||
/// delete it. This aborts if the value isn't in the table.
|
||||
void StringMapImpl::RemoveKey(StringMapEntryBase *V) {
|
||||
const char *VStr = (char*)V + ItemSize;
|
||||
StringMapEntryBase *V2 = RemoveKey(StringRef(VStr, V->getKeyLength()));
|
||||
(void)V2;
|
||||
assert(V == V2 && "Didn't find key?");
|
||||
}
|
||||
|
||||
/// RemoveKey - Remove the StringMapEntry for the specified key from the
|
||||
/// table, returning it. If the key is not in the table, this returns null.
|
||||
StringMapEntryBase *StringMapImpl::RemoveKey(StringRef Key) {
|
||||
int Bucket = FindKey(Key);
|
||||
if (Bucket == -1) return nullptr;
|
||||
|
||||
StringMapEntryBase *Result = TheTable[Bucket];
|
||||
TheTable[Bucket] = getTombstoneVal();
|
||||
--NumItems;
|
||||
++NumTombstones;
|
||||
assert(NumItems + NumTombstones <= NumBuckets);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// RehashTable - Grow the table, redistributing values into the buckets with
|
||||
/// the appropriate mod-of-hashtable-size.
|
||||
unsigned StringMapImpl::RehashTable(unsigned BucketNo) {
|
||||
unsigned NewSize;
|
||||
unsigned *HashTable = (unsigned *)(TheTable + NumBuckets + 1);
|
||||
|
||||
// If the hash table is now more than 3/4 full, or if fewer than 1/8 of
|
||||
// the buckets are empty (meaning that many are filled with tombstones),
|
||||
// grow/rehash the table.
|
||||
if (LLVM_UNLIKELY(NumItems * 4 > NumBuckets * 3)) {
|
||||
NewSize = NumBuckets*2;
|
||||
} else if (LLVM_UNLIKELY(NumBuckets - (NumItems + NumTombstones) <=
|
||||
NumBuckets / 8)) {
|
||||
NewSize = NumBuckets;
|
||||
} else {
|
||||
return BucketNo;
|
||||
}
|
||||
|
||||
unsigned NewBucketNo = BucketNo;
|
||||
// Allocate one extra bucket which will always be non-empty. This allows the
|
||||
// iterators to stop at end.
|
||||
StringMapEntryBase **NewTableArray =
|
||||
(StringMapEntryBase **)calloc(NewSize+1, sizeof(StringMapEntryBase *) +
|
||||
sizeof(unsigned));
|
||||
unsigned *NewHashArray = (unsigned *)(NewTableArray + NewSize + 1);
|
||||
NewTableArray[NewSize] = (StringMapEntryBase*)2;
|
||||
|
||||
// Rehash all the items into their new buckets. Luckily :) we already have
|
||||
// the hash values available, so we don't have to rehash any strings.
|
||||
for (unsigned I = 0, E = NumBuckets; I != E; ++I) {
|
||||
StringMapEntryBase *Bucket = TheTable[I];
|
||||
if (Bucket && Bucket != getTombstoneVal()) {
|
||||
// Fast case, bucket available.
|
||||
unsigned FullHash = HashTable[I];
|
||||
unsigned NewBucket = FullHash & (NewSize-1);
|
||||
if (!NewTableArray[NewBucket]) {
|
||||
NewTableArray[FullHash & (NewSize-1)] = Bucket;
|
||||
NewHashArray[FullHash & (NewSize-1)] = FullHash;
|
||||
if (I == BucketNo)
|
||||
NewBucketNo = NewBucket;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise probe for a spot.
|
||||
unsigned ProbeSize = 1;
|
||||
do {
|
||||
NewBucket = (NewBucket + ProbeSize++) & (NewSize-1);
|
||||
} while (NewTableArray[NewBucket]);
|
||||
|
||||
// Finally found a slot. Fill it in.
|
||||
NewTableArray[NewBucket] = Bucket;
|
||||
NewHashArray[NewBucket] = FullHash;
|
||||
if (I == BucketNo)
|
||||
NewBucketNo = NewBucket;
|
||||
}
|
||||
}
|
||||
|
||||
free(TheTable);
|
||||
|
||||
TheTable = NewTableArray;
|
||||
NumBuckets = NewSize;
|
||||
NumTombstones = 0;
|
||||
return NewBucketNo;
|
||||
}
|
||||
452
wpiutil/src/main/native/cpp/llvm/StringRef.cpp
Normal file
452
wpiutil/src/main/native/cpp/llvm/StringRef.cpp
Normal file
@@ -0,0 +1,452 @@
|
||||
//===-- StringRef.cpp - Lightweight String References ---------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/StringRef.h"
|
||||
#include "llvm/Hashing.h"
|
||||
#include "llvm/SmallVector.h"
|
||||
#include <bitset>
|
||||
#include <climits>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
// MSVC emits references to this into the translation units which reference it.
|
||||
#ifndef _MSC_VER
|
||||
const size_t StringRef::npos;
|
||||
#endif
|
||||
|
||||
static char ascii_tolower(char x) {
|
||||
if (x >= 'A' && x <= 'Z')
|
||||
return x - 'A' + 'a';
|
||||
return x;
|
||||
}
|
||||
|
||||
static char ascii_toupper(char x) {
|
||||
if (x >= 'a' && x <= 'z')
|
||||
return x - 'a' + 'A';
|
||||
return x;
|
||||
}
|
||||
|
||||
static bool ascii_isdigit(char x) {
|
||||
return x >= '0' && x <= '9';
|
||||
}
|
||||
|
||||
// strncasecmp() is not available on non-POSIX systems, so define an
|
||||
// alternative function here.
|
||||
static int ascii_strncasecmp(const char *LHS, const char *RHS, size_t Length) {
|
||||
for (size_t I = 0; I < Length; ++I) {
|
||||
unsigned char LHC = ascii_tolower(LHS[I]);
|
||||
unsigned char RHC = ascii_tolower(RHS[I]);
|
||||
if (LHC != RHC)
|
||||
return LHC < RHC ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// compare_lower - Compare strings, ignoring case.
|
||||
int StringRef::compare_lower(StringRef RHS) const {
|
||||
if (int Res = ascii_strncasecmp(Data, RHS.Data, std::min(size(), RHS.size())))
|
||||
return Res;
|
||||
if (size() == RHS.size())
|
||||
return 0;
|
||||
return size() < RHS.size() ? -1 : 1;
|
||||
}
|
||||
|
||||
/// Check if this string starts with the given \p Prefix, ignoring case.
|
||||
bool StringRef::startswith_lower(StringRef Prefix) const {
|
||||
return size() >= Prefix.size() &&
|
||||
ascii_strncasecmp(Data, Prefix.Data, Prefix.size()) == 0;
|
||||
}
|
||||
|
||||
/// Check if this string ends with the given \p Suffix, ignoring case.
|
||||
bool StringRef::endswith_lower(StringRef Suffix) const {
|
||||
return size() >= Suffix.size() &&
|
||||
ascii_strncasecmp(end() - Suffix.size(), Suffix.Data, Suffix.size()) == 0;
|
||||
}
|
||||
|
||||
/// compare_numeric - Compare strings, handle embedded numbers.
|
||||
int StringRef::compare_numeric(StringRef RHS) const {
|
||||
for (size_t I = 0, E = std::min(size(), RHS.size()); I != E; ++I) {
|
||||
// Check for sequences of digits.
|
||||
if (ascii_isdigit(Data[I]) && ascii_isdigit(RHS.Data[I])) {
|
||||
// The longer sequence of numbers is considered larger.
|
||||
// This doesn't really handle prefixed zeros well.
|
||||
size_t J;
|
||||
for (J = I + 1; J != E + 1; ++J) {
|
||||
bool ld = J < size() && ascii_isdigit(Data[J]);
|
||||
bool rd = J < RHS.size() && ascii_isdigit(RHS.Data[J]);
|
||||
if (ld != rd)
|
||||
return rd ? -1 : 1;
|
||||
if (!rd)
|
||||
break;
|
||||
}
|
||||
// The two number sequences have the same length (J-I), just memcmp them.
|
||||
if (int Res = compareMemory(Data + I, RHS.Data + I, J - I))
|
||||
return Res < 0 ? -1 : 1;
|
||||
// Identical number sequences, continue search after the numbers.
|
||||
I = J - 1;
|
||||
continue;
|
||||
}
|
||||
if (Data[I] != RHS.Data[I])
|
||||
return (unsigned char)Data[I] < (unsigned char)RHS.Data[I] ? -1 : 1;
|
||||
}
|
||||
if (size() == RHS.size())
|
||||
return 0;
|
||||
return size() < RHS.size() ? -1 : 1;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// String Operations
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
std::string StringRef::lower() const {
|
||||
std::string Result(size(), char());
|
||||
for (size_type i = 0, e = size(); i != e; ++i) {
|
||||
Result[i] = ascii_tolower(Data[i]);
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
std::string StringRef::upper() const {
|
||||
std::string Result(size(), char());
|
||||
for (size_type i = 0, e = size(); i != e; ++i) {
|
||||
Result[i] = ascii_toupper(Data[i]);
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
const char *StringRef::c_str(llvm::SmallVectorImpl<char>& buf) const {
|
||||
if (is_null_terminated()) {
|
||||
// If null terminated, return data directly
|
||||
return data();
|
||||
} else {
|
||||
// If not null terminated, use SmallVectorImpl to store data
|
||||
// copy data, and return a known null terminated string
|
||||
buf.clear();
|
||||
buf.append(begin(), end());
|
||||
buf.push_back(0);
|
||||
return buf.begin();
|
||||
}
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// String Searching
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
|
||||
/// find - Search for the first string \arg Str in the string.
|
||||
///
|
||||
/// \return - The index of the first occurrence of \arg Str, or npos if not
|
||||
/// found.
|
||||
size_t StringRef::find(StringRef Str, size_t From) const {
|
||||
if (From > size())
|
||||
return npos;
|
||||
|
||||
const char *Needle = Str.data();
|
||||
size_t N = Str.size();
|
||||
if (N == 0)
|
||||
return From;
|
||||
|
||||
size_t Size = size() - From;
|
||||
if (Size < N)
|
||||
return npos;
|
||||
|
||||
const char *Start = Data + From;
|
||||
const char *Stop = Start + (Size - N + 1);
|
||||
|
||||
// For short haystacks or unsupported needles fall back to the naive algorithm
|
||||
if (Size < 16 || N > 255) {
|
||||
do {
|
||||
if (std::memcmp(Start, Needle, N) == 0)
|
||||
return Start - Data;
|
||||
++Start;
|
||||
} while (Start < Stop);
|
||||
return npos;
|
||||
}
|
||||
|
||||
// Build the bad char heuristic table, with uint8_t to reduce cache thrashing.
|
||||
uint8_t BadCharSkip[256];
|
||||
std::memset(BadCharSkip, N, 256);
|
||||
for (unsigned i = 0; i != N-1; ++i)
|
||||
BadCharSkip[(uint8_t)Str[i]] = N-1-i;
|
||||
|
||||
do {
|
||||
if (std::memcmp(Start, Needle, N) == 0)
|
||||
return Start - Data;
|
||||
|
||||
// Otherwise skip the appropriate number of bytes.
|
||||
Start += BadCharSkip[(uint8_t)Start[N-1]];
|
||||
} while (Start < Stop);
|
||||
|
||||
return npos;
|
||||
}
|
||||
|
||||
/// rfind - Search for the last string \arg Str in the string.
|
||||
///
|
||||
/// \return - The index of the last occurrence of \arg Str, or npos if not
|
||||
/// found.
|
||||
size_t StringRef::rfind(StringRef Str) const {
|
||||
size_t N = Str.size();
|
||||
if (N > size())
|
||||
return npos;
|
||||
for (size_t i = size() - N + 1, e = 0; i != e;) {
|
||||
--i;
|
||||
if (substr(i, N).equals(Str))
|
||||
return i;
|
||||
}
|
||||
return npos;
|
||||
}
|
||||
|
||||
/// find_first_of - Find the first character in the string that is in \arg
|
||||
/// Chars, or npos if not found.
|
||||
///
|
||||
/// Note: O(size() + Chars.size())
|
||||
StringRef::size_type StringRef::find_first_of(StringRef Chars,
|
||||
size_t From) const {
|
||||
std::bitset<1 << CHAR_BIT> CharBits;
|
||||
for (size_type i = 0; i != Chars.size(); ++i)
|
||||
CharBits.set((unsigned char)Chars[i]);
|
||||
|
||||
for (size_type i = std::min(From, size()), e = size(); i != e; ++i)
|
||||
if (CharBits.test((unsigned char)Data[i]))
|
||||
return i;
|
||||
return npos;
|
||||
}
|
||||
|
||||
/// find_first_not_of - Find the first character in the string that is not
|
||||
/// \arg C or npos if not found.
|
||||
StringRef::size_type StringRef::find_first_not_of(char C, size_t From) const {
|
||||
for (size_type i = std::min(From, size()), e = size(); i != e; ++i)
|
||||
if (Data[i] != C)
|
||||
return i;
|
||||
return npos;
|
||||
}
|
||||
|
||||
/// find_first_not_of - Find the first character in the string that is not
|
||||
/// in the string \arg Chars, or npos if not found.
|
||||
///
|
||||
/// Note: O(size() + Chars.size())
|
||||
StringRef::size_type StringRef::find_first_not_of(StringRef Chars,
|
||||
size_t From) const {
|
||||
std::bitset<1 << CHAR_BIT> CharBits;
|
||||
for (size_type i = 0; i != Chars.size(); ++i)
|
||||
CharBits.set((unsigned char)Chars[i]);
|
||||
|
||||
for (size_type i = std::min(From, size()), e = size(); i != e; ++i)
|
||||
if (!CharBits.test((unsigned char)Data[i]))
|
||||
return i;
|
||||
return npos;
|
||||
}
|
||||
|
||||
/// find_last_of - Find the last character in the string that is in \arg C,
|
||||
/// or npos if not found.
|
||||
///
|
||||
/// Note: O(size() + Chars.size())
|
||||
StringRef::size_type StringRef::find_last_of(StringRef Chars,
|
||||
size_t From) const {
|
||||
std::bitset<1 << CHAR_BIT> CharBits;
|
||||
for (size_type i = 0; i != Chars.size(); ++i)
|
||||
CharBits.set((unsigned char)Chars[i]);
|
||||
|
||||
for (size_type i = std::min(From, size()) - 1, e = -1; i != e; --i)
|
||||
if (CharBits.test((unsigned char)Data[i]))
|
||||
return i;
|
||||
return npos;
|
||||
}
|
||||
|
||||
/// find_last_not_of - Find the last character in the string that is not
|
||||
/// \arg C, or npos if not found.
|
||||
StringRef::size_type StringRef::find_last_not_of(char C, size_t From) const {
|
||||
for (size_type i = std::min(From, size()) - 1, e = -1; i != e; --i)
|
||||
if (Data[i] != C)
|
||||
return i;
|
||||
return npos;
|
||||
}
|
||||
|
||||
/// find_last_not_of - Find the last character in the string that is not in
|
||||
/// \arg Chars, or npos if not found.
|
||||
///
|
||||
/// Note: O(size() + Chars.size())
|
||||
StringRef::size_type StringRef::find_last_not_of(StringRef Chars,
|
||||
size_t From) const {
|
||||
std::bitset<1 << CHAR_BIT> CharBits;
|
||||
for (size_type i = 0, e = Chars.size(); i != e; ++i)
|
||||
CharBits.set((unsigned char)Chars[i]);
|
||||
|
||||
for (size_type i = std::min(From, size()) - 1, e = -1; i != e; --i)
|
||||
if (!CharBits.test((unsigned char)Data[i]))
|
||||
return i;
|
||||
return npos;
|
||||
}
|
||||
|
||||
void StringRef::split(SmallVectorImpl<StringRef> &A,
|
||||
StringRef Separator, int MaxSplit,
|
||||
bool KeepEmpty) const {
|
||||
StringRef S = *this;
|
||||
|
||||
// Count down from MaxSplit. When MaxSplit is -1, this will just split
|
||||
// "forever". This doesn't support splitting more than 2^31 times
|
||||
// intentionally; if we ever want that we can make MaxSplit a 64-bit integer
|
||||
// but that seems unlikely to be useful.
|
||||
while (MaxSplit-- != 0) {
|
||||
size_t Idx = S.find(Separator);
|
||||
if (Idx == npos)
|
||||
break;
|
||||
|
||||
// Push this split.
|
||||
if (KeepEmpty || Idx > 0)
|
||||
A.push_back(S.slice(0, Idx));
|
||||
|
||||
// Jump forward.
|
||||
S = S.slice(Idx + Separator.size(), npos);
|
||||
}
|
||||
|
||||
// Push the tail.
|
||||
if (KeepEmpty || !S.empty())
|
||||
A.push_back(S);
|
||||
}
|
||||
|
||||
void StringRef::split(SmallVectorImpl<StringRef> &A, char Separator,
|
||||
int MaxSplit, bool KeepEmpty) const {
|
||||
StringRef S = *this;
|
||||
|
||||
// Count down from MaxSplit. When MaxSplit is -1, this will just split
|
||||
// "forever". This doesn't support splitting more than 2^31 times
|
||||
// intentionally; if we ever want that we can make MaxSplit a 64-bit integer
|
||||
// but that seems unlikely to be useful.
|
||||
while (MaxSplit-- != 0) {
|
||||
size_t Idx = S.find(Separator);
|
||||
if (Idx == npos)
|
||||
break;
|
||||
|
||||
// Push this split.
|
||||
if (KeepEmpty || Idx > 0)
|
||||
A.push_back(S.slice(0, Idx));
|
||||
|
||||
// Jump forward.
|
||||
S = S.slice(Idx + 1, npos);
|
||||
}
|
||||
|
||||
// Push the tail.
|
||||
if (KeepEmpty || !S.empty())
|
||||
A.push_back(S);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Helpful Algorithms
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// count - Return the number of non-overlapped occurrences of \arg Str in
|
||||
/// the string.
|
||||
size_t StringRef::count(StringRef Str) const {
|
||||
size_t Count = 0;
|
||||
size_t N = Str.size();
|
||||
if (N > size())
|
||||
return 0;
|
||||
for (size_t i = 0, e = size() - N + 1; i != e; ++i)
|
||||
if (substr(i, N).equals(Str))
|
||||
++Count;
|
||||
return Count;
|
||||
}
|
||||
|
||||
static unsigned GetAutoSenseRadix(StringRef &Str) {
|
||||
if (Str.startswith("0x") || Str.startswith("0X")) {
|
||||
Str = Str.substr(2);
|
||||
return 16;
|
||||
}
|
||||
|
||||
if (Str.startswith("0b") || Str.startswith("0B")) {
|
||||
Str = Str.substr(2);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (Str.startswith("0o")) {
|
||||
Str = Str.substr(2);
|
||||
return 8;
|
||||
}
|
||||
|
||||
if (Str.startswith("0"))
|
||||
return 8;
|
||||
|
||||
return 10;
|
||||
}
|
||||
|
||||
|
||||
/// GetAsUnsignedInteger - Workhorse method that converts a integer character
|
||||
/// sequence of radix up to 36 to an unsigned long long value.
|
||||
bool llvm::getAsUnsignedInteger(StringRef Str, unsigned Radix,
|
||||
unsigned long long &Result) {
|
||||
// Autosense radix if not specified.
|
||||
if (Radix == 0)
|
||||
Radix = GetAutoSenseRadix(Str);
|
||||
|
||||
// Empty strings (after the radix autosense) are invalid.
|
||||
if (Str.empty()) return true;
|
||||
|
||||
// Parse all the bytes of the string given this radix. Watch for overflow.
|
||||
Result = 0;
|
||||
while (!Str.empty()) {
|
||||
unsigned CharVal;
|
||||
if (Str[0] >= '0' && Str[0] <= '9')
|
||||
CharVal = Str[0]-'0';
|
||||
else if (Str[0] >= 'a' && Str[0] <= 'z')
|
||||
CharVal = Str[0]-'a'+10;
|
||||
else if (Str[0] >= 'A' && Str[0] <= 'Z')
|
||||
CharVal = Str[0]-'A'+10;
|
||||
else
|
||||
return true;
|
||||
|
||||
// If the parsed value is larger than the integer radix, the string is
|
||||
// invalid.
|
||||
if (CharVal >= Radix)
|
||||
return true;
|
||||
|
||||
// Add in this character.
|
||||
unsigned long long PrevResult = Result;
|
||||
Result = Result*Radix+CharVal;
|
||||
|
||||
// Check for overflow by shifting back and seeing if bits were lost.
|
||||
if (Result/Radix < PrevResult)
|
||||
return true;
|
||||
|
||||
Str = Str.substr(1);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool llvm::getAsSignedInteger(StringRef Str, unsigned Radix,
|
||||
long long &Result) {
|
||||
unsigned long long ULLVal;
|
||||
|
||||
// Handle positive strings first.
|
||||
if (Str.empty() || Str.front() != '-') {
|
||||
if (getAsUnsignedInteger(Str, Radix, ULLVal) ||
|
||||
// Check for value so large it overflows a signed value.
|
||||
(long long)ULLVal < 0)
|
||||
return true;
|
||||
Result = ULLVal;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the positive part of the value.
|
||||
if (getAsUnsignedInteger(Str.substr(1), Radix, ULLVal) ||
|
||||
// Reject values so large they'd overflow as negative signed, but allow
|
||||
// "-0". This negates the unsigned so that the negative isn't undefined
|
||||
// on signed overflow.
|
||||
(long long)-ULLVal > 0)
|
||||
return true;
|
||||
|
||||
Result = -ULLVal;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Implementation of StringRef hashing.
|
||||
hash_code llvm::hash_value(StringRef S) {
|
||||
return hash_combine_range(S.begin(), S.end());
|
||||
}
|
||||
169
wpiutil/src/main/native/cpp/llvm/Twine.cpp
Normal file
169
wpiutil/src/main/native/cpp/llvm/Twine.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
//===-- Twine.cpp - Fast Temporary String Concatenation -------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/Twine.h"
|
||||
#include "llvm/SmallString.h"
|
||||
#include "llvm/raw_ostream.h"
|
||||
using namespace llvm;
|
||||
|
||||
std::string Twine::str() const {
|
||||
// If we're storing only a std::string, just return it.
|
||||
if (LHSKind == StdStringKind && RHSKind == EmptyKind)
|
||||
return *LHS.stdString;
|
||||
|
||||
// Otherwise, flatten and copy the contents first.
|
||||
SmallString<256> Vec;
|
||||
return toStringRef(Vec).str();
|
||||
}
|
||||
|
||||
void Twine::toVector(SmallVectorImpl<char> &Out) const {
|
||||
raw_svector_ostream OS(Out);
|
||||
print(OS);
|
||||
}
|
||||
|
||||
StringRef Twine::toNullTerminatedStringRef(SmallVectorImpl<char> &Out) const {
|
||||
if (isUnary()) {
|
||||
switch (getLHSKind()) {
|
||||
case CStringKind:
|
||||
// Already null terminated, yay!
|
||||
return StringRef(LHS.cString);
|
||||
case StdStringKind: {
|
||||
const std::string *str = LHS.stdString;
|
||||
return StringRef(str->c_str(), str->size());
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
toVector(Out);
|
||||
Out.push_back(0);
|
||||
Out.pop_back();
|
||||
return StringRef(Out.data(), Out.size());
|
||||
}
|
||||
|
||||
void Twine::printOneChild(raw_ostream &OS, Child Ptr,
|
||||
NodeKind Kind) const {
|
||||
switch (Kind) {
|
||||
case Twine::NullKind: break;
|
||||
case Twine::EmptyKind: break;
|
||||
case Twine::TwineKind:
|
||||
Ptr.twine->print(OS);
|
||||
break;
|
||||
case Twine::CStringKind:
|
||||
OS << Ptr.cString;
|
||||
break;
|
||||
case Twine::StdStringKind:
|
||||
OS << *Ptr.stdString;
|
||||
break;
|
||||
case Twine::StringRefKind:
|
||||
OS << *Ptr.stringRef;
|
||||
break;
|
||||
case Twine::SmallStringKind:
|
||||
OS << *Ptr.smallString;
|
||||
break;
|
||||
case Twine::CharKind:
|
||||
OS << Ptr.character;
|
||||
break;
|
||||
case Twine::DecUIKind:
|
||||
OS << Ptr.decUI;
|
||||
break;
|
||||
case Twine::DecIKind:
|
||||
OS << Ptr.decI;
|
||||
break;
|
||||
case Twine::DecULKind:
|
||||
OS << *Ptr.decUL;
|
||||
break;
|
||||
case Twine::DecLKind:
|
||||
OS << *Ptr.decL;
|
||||
break;
|
||||
case Twine::DecULLKind:
|
||||
OS << *Ptr.decULL;
|
||||
break;
|
||||
case Twine::DecLLKind:
|
||||
OS << *Ptr.decLL;
|
||||
break;
|
||||
case Twine::UHexKind:
|
||||
OS.write_hex(*Ptr.uHex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Twine::printOneChildRepr(raw_ostream &OS, Child Ptr,
|
||||
NodeKind Kind) const {
|
||||
switch (Kind) {
|
||||
case Twine::NullKind:
|
||||
OS << "null"; break;
|
||||
case Twine::EmptyKind:
|
||||
OS << "empty"; break;
|
||||
case Twine::TwineKind:
|
||||
OS << "rope:";
|
||||
Ptr.twine->printRepr(OS);
|
||||
break;
|
||||
case Twine::CStringKind:
|
||||
OS << "cstring:\""
|
||||
<< Ptr.cString << "\"";
|
||||
break;
|
||||
case Twine::StdStringKind:
|
||||
OS << "std::string:\""
|
||||
<< Ptr.stdString << "\"";
|
||||
break;
|
||||
case Twine::StringRefKind:
|
||||
OS << "stringref:\""
|
||||
<< Ptr.stringRef << "\"";
|
||||
break;
|
||||
case Twine::SmallStringKind:
|
||||
OS << "smallstring:\"" << *Ptr.smallString << "\"";
|
||||
break;
|
||||
case Twine::CharKind:
|
||||
OS << "char:\"" << Ptr.character << "\"";
|
||||
break;
|
||||
case Twine::DecUIKind:
|
||||
OS << "decUI:\"" << Ptr.decUI << "\"";
|
||||
break;
|
||||
case Twine::DecIKind:
|
||||
OS << "decI:\"" << Ptr.decI << "\"";
|
||||
break;
|
||||
case Twine::DecULKind:
|
||||
OS << "decUL:\"" << *Ptr.decUL << "\"";
|
||||
break;
|
||||
case Twine::DecLKind:
|
||||
OS << "decL:\"" << *Ptr.decL << "\"";
|
||||
break;
|
||||
case Twine::DecULLKind:
|
||||
OS << "decULL:\"" << *Ptr.decULL << "\"";
|
||||
break;
|
||||
case Twine::DecLLKind:
|
||||
OS << "decLL:\"" << *Ptr.decLL << "\"";
|
||||
break;
|
||||
case Twine::UHexKind:
|
||||
OS << "uhex:\"" << Ptr.uHex << "\"";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Twine::print(raw_ostream &OS) const {
|
||||
printOneChild(OS, LHS, getLHSKind());
|
||||
printOneChild(OS, RHS, getRHSKind());
|
||||
}
|
||||
|
||||
void Twine::printRepr(raw_ostream &OS) const {
|
||||
OS << "(Twine ";
|
||||
printOneChildRepr(OS, LHS, getLHSKind());
|
||||
OS << " ";
|
||||
printOneChildRepr(OS, RHS, getRHSKind());
|
||||
OS << ")";
|
||||
}
|
||||
|
||||
void Twine::dump() const {
|
||||
print(errs());
|
||||
}
|
||||
|
||||
void Twine::dumpRepr() const {
|
||||
printRepr(errs());
|
||||
}
|
||||
390
wpiutil/src/main/native/cpp/llvm/Unix/Path.inc
Normal file
390
wpiutil/src/main/native/cpp/llvm/Unix/Path.inc
Normal file
@@ -0,0 +1,390 @@
|
||||
//===- llvm/Support/Unix/Path.inc - Unix Path Implementation ----*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements the Unix specific implementation of the Path API.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
//=== WARNING: Implementation here must contain only generic UNIX code that
|
||||
//=== is guaranteed to work on *all* UNIX variants.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
#define NAMLEN(dirent) strlen((dirent)->d_name)
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace llvm {
|
||||
namespace sys {
|
||||
namespace fs {
|
||||
UniqueID file_status::getUniqueID() const {
|
||||
return UniqueID(fs_st_dev, fs_st_ino);
|
||||
}
|
||||
|
||||
std::error_code current_path(SmallVectorImpl<char> &result) {
|
||||
result.clear();
|
||||
|
||||
const char *pwd = ::getenv("PWD");
|
||||
llvm::sys::fs::file_status PWDStatus, DotStatus;
|
||||
if (pwd && llvm::sys::path::is_absolute(pwd) &&
|
||||
!llvm::sys::fs::status(pwd, PWDStatus) &&
|
||||
!llvm::sys::fs::status(".", DotStatus) &&
|
||||
PWDStatus.getUniqueID() == DotStatus.getUniqueID()) {
|
||||
result.append(pwd, pwd + strlen(pwd));
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
#ifdef MAXPATHLEN
|
||||
result.reserve(MAXPATHLEN);
|
||||
#else
|
||||
result.reserve(1024);
|
||||
#endif
|
||||
|
||||
while (true) {
|
||||
if (::getcwd(result.data(), result.capacity()) == nullptr) {
|
||||
// See if there was a real error.
|
||||
if (errno != ENOMEM)
|
||||
return std::error_code(errno, std::generic_category());
|
||||
// Otherwise there just wasn't enough space.
|
||||
result.reserve(result.capacity() * 2);
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
result.set_size(strlen(result.data()));
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
static int convertAccessMode(AccessMode Mode) {
|
||||
switch (Mode) {
|
||||
case AccessMode::Exist:
|
||||
return F_OK;
|
||||
case AccessMode::Write:
|
||||
return W_OK;
|
||||
case AccessMode::Execute:
|
||||
return R_OK | X_OK; // scripts also need R_OK.
|
||||
default:
|
||||
return F_OK;
|
||||
}
|
||||
}
|
||||
|
||||
std::error_code access(const Twine &Path, AccessMode Mode) {
|
||||
SmallString<128> PathStorage;
|
||||
StringRef P = Path.toNullTerminatedStringRef(PathStorage);
|
||||
|
||||
if (::access(P.begin(), convertAccessMode(Mode)) == -1)
|
||||
return std::error_code(errno, std::generic_category());
|
||||
|
||||
if (Mode == AccessMode::Execute) {
|
||||
// Don't say that directories are executable.
|
||||
struct stat buf;
|
||||
if (0 != stat(P.begin(), &buf))
|
||||
return std::make_error_code(std::errc::permission_denied);
|
||||
if (!S_ISREG(buf.st_mode))
|
||||
return std::make_error_code(std::errc::permission_denied);
|
||||
}
|
||||
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
bool equivalent(file_status A, file_status B) {
|
||||
assert(status_known(A) && status_known(B));
|
||||
return A.fs_st_dev == B.fs_st_dev &&
|
||||
A.fs_st_ino == B.fs_st_ino;
|
||||
}
|
||||
|
||||
std::error_code equivalent(const Twine &A, const Twine &B, bool &result) {
|
||||
file_status fsA, fsB;
|
||||
if (std::error_code ec = status(A, fsA))
|
||||
return ec;
|
||||
if (std::error_code ec = status(B, fsB))
|
||||
return ec;
|
||||
result = equivalent(fsA, fsB);
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
static std::error_code fillStatus(int StatRet, const struct stat &Status,
|
||||
file_status &Result) {
|
||||
if (StatRet != 0) {
|
||||
std::error_code ec(errno, std::generic_category());
|
||||
if (ec == std::errc::no_such_file_or_directory)
|
||||
Result = file_status(file_type::file_not_found);
|
||||
else
|
||||
Result = file_status(file_type::status_error);
|
||||
return ec;
|
||||
}
|
||||
|
||||
file_type Type = file_type::type_unknown;
|
||||
|
||||
if (S_ISDIR(Status.st_mode))
|
||||
Type = file_type::directory_file;
|
||||
else if (S_ISREG(Status.st_mode))
|
||||
Type = file_type::regular_file;
|
||||
else if (S_ISBLK(Status.st_mode))
|
||||
Type = file_type::block_file;
|
||||
else if (S_ISCHR(Status.st_mode))
|
||||
Type = file_type::character_file;
|
||||
else if (S_ISFIFO(Status.st_mode))
|
||||
Type = file_type::fifo_file;
|
||||
else if (S_ISSOCK(Status.st_mode))
|
||||
Type = file_type::socket_file;
|
||||
else if (S_ISLNK(Status.st_mode))
|
||||
Type = file_type::symlink_file;
|
||||
|
||||
perms Perms = static_cast<perms>(Status.st_mode);
|
||||
Result =
|
||||
file_status(Type, Perms, Status.st_dev, Status.st_ino, Status.st_atime,
|
||||
Status.st_mtime, Status.st_uid, Status.st_gid,
|
||||
Status.st_size);
|
||||
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
std::error_code status(const Twine &Path, file_status &Result) {
|
||||
SmallString<128> PathStorage;
|
||||
StringRef P = Path.toNullTerminatedStringRef(PathStorage);
|
||||
|
||||
struct stat Status;
|
||||
int StatRet = ::lstat(P.begin(), &Status);
|
||||
return fillStatus(StatRet, Status, Result);
|
||||
}
|
||||
|
||||
std::error_code status(int FD, file_status &Result) {
|
||||
struct stat Status;
|
||||
int StatRet = ::fstat(FD, &Status);
|
||||
return fillStatus(StatRet, Status, Result);
|
||||
}
|
||||
|
||||
std::error_code detail::directory_iterator_construct(detail::DirIterState &it,
|
||||
StringRef path){
|
||||
SmallString<128> path_null(path);
|
||||
DIR *directory = ::opendir(path_null.c_str());
|
||||
if (!directory)
|
||||
return std::error_code(errno, std::generic_category());
|
||||
|
||||
it.IterationHandle = reinterpret_cast<intptr_t>(directory);
|
||||
// Add something for replace_filename to replace.
|
||||
path::append(path_null, ".");
|
||||
it.CurrentEntry = directory_entry(path_null.str());
|
||||
return directory_iterator_increment(it);
|
||||
}
|
||||
|
||||
std::error_code detail::directory_iterator_destruct(detail::DirIterState &it) {
|
||||
if (it.IterationHandle)
|
||||
::closedir(reinterpret_cast<DIR *>(it.IterationHandle));
|
||||
it.IterationHandle = 0;
|
||||
it.CurrentEntry = directory_entry();
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
std::error_code detail::directory_iterator_increment(detail::DirIterState &it) {
|
||||
errno = 0;
|
||||
dirent *cur_dir = ::readdir(reinterpret_cast<DIR *>(it.IterationHandle));
|
||||
if (cur_dir == nullptr && errno != 0) {
|
||||
return std::error_code(errno, std::generic_category());
|
||||
} else if (cur_dir != nullptr) {
|
||||
StringRef name(cur_dir->d_name, NAMLEN(cur_dir));
|
||||
if ((name.size() == 1 && name[0] == '.') ||
|
||||
(name.size() == 2 && name[0] == '.' && name[1] == '.'))
|
||||
return directory_iterator_increment(it);
|
||||
it.CurrentEntry.replace_filename(name);
|
||||
} else
|
||||
return directory_iterator_destruct(it);
|
||||
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
#if !defined(F_GETPATH)
|
||||
static bool hasProcSelfFD() {
|
||||
// If we have a /proc filesystem mounted, we can quickly establish the
|
||||
// real name of the file with readlink
|
||||
static const bool Result = (::access("/proc/self/fd", R_OK) == 0);
|
||||
return Result;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::error_code openFileForRead(const Twine &Name, int &ResultFD,
|
||||
SmallVectorImpl<char> *RealPath) {
|
||||
SmallString<128> Storage;
|
||||
StringRef P = Name.toNullTerminatedStringRef(Storage);
|
||||
while ((ResultFD = open(P.begin(), O_RDONLY)) < 0) {
|
||||
if (errno != EINTR)
|
||||
return std::error_code(errno, std::generic_category());
|
||||
}
|
||||
// Attempt to get the real name of the file, if the user asked
|
||||
if(!RealPath)
|
||||
return std::error_code();
|
||||
RealPath->clear();
|
||||
#if defined(F_GETPATH)
|
||||
// When F_GETPATH is availble, it is the quickest way to get
|
||||
// the real path name.
|
||||
char Buffer[MAXPATHLEN];
|
||||
if (::fcntl(ResultFD, F_GETPATH, Buffer) != -1)
|
||||
RealPath->append(Buffer, Buffer + strlen(Buffer));
|
||||
#else
|
||||
char Buffer[PATH_MAX];
|
||||
if (hasProcSelfFD()) {
|
||||
char ProcPath[64];
|
||||
snprintf(ProcPath, sizeof(ProcPath), "/proc/self/fd/%d", ResultFD);
|
||||
ssize_t CharCount = ::readlink(ProcPath, Buffer, sizeof(Buffer));
|
||||
if (CharCount > 0)
|
||||
RealPath->append(Buffer, Buffer + CharCount);
|
||||
} else {
|
||||
// Use ::realpath to get the real path name
|
||||
if (::realpath(P.begin(), Buffer) != nullptr)
|
||||
RealPath->append(Buffer, Buffer + strlen(Buffer));
|
||||
}
|
||||
#endif
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
std::error_code openFileForWrite(const Twine &Name, int &ResultFD,
|
||||
OpenFlags Flags, unsigned Mode) {
|
||||
// Verify that we don't have both "append" and "excl".
|
||||
assert((!(Flags & F_Excl) || !(Flags & F_Append)) &&
|
||||
"Cannot specify both 'excl' and 'append' file creation flags!");
|
||||
|
||||
int OpenFlags = O_CREAT;
|
||||
|
||||
if (Flags & F_RW)
|
||||
OpenFlags |= O_RDWR;
|
||||
else
|
||||
OpenFlags |= O_WRONLY;
|
||||
|
||||
if (Flags & F_Append)
|
||||
OpenFlags |= O_APPEND;
|
||||
else
|
||||
OpenFlags |= O_TRUNC;
|
||||
|
||||
if (Flags & F_Excl)
|
||||
OpenFlags |= O_EXCL;
|
||||
|
||||
SmallString<128> Storage;
|
||||
StringRef P = Name.toNullTerminatedStringRef(Storage);
|
||||
while ((ResultFD = open(P.begin(), OpenFlags, Mode)) < 0) {
|
||||
if (errno != EINTR)
|
||||
return std::error_code(errno, std::generic_category());
|
||||
}
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
} // end namespace fs
|
||||
|
||||
namespace path {
|
||||
|
||||
bool home_directory(SmallVectorImpl<char> &result) {
|
||||
if (char *RequestedDir = std::getenv("HOME")) {
|
||||
result.clear();
|
||||
result.append(RequestedDir, RequestedDir + strlen(RequestedDir));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool getDarwinConfDir(bool TempDir, SmallVectorImpl<char> &Result) {
|
||||
#if defined(_CS_DARWIN_USER_TEMP_DIR) && defined(_CS_DARWIN_USER_CACHE_DIR)
|
||||
// On Darwin, use DARWIN_USER_TEMP_DIR or DARWIN_USER_CACHE_DIR.
|
||||
// macros defined in <unistd.h> on darwin >= 9
|
||||
int ConfName = TempDir ? _CS_DARWIN_USER_TEMP_DIR
|
||||
: _CS_DARWIN_USER_CACHE_DIR;
|
||||
size_t ConfLen = confstr(ConfName, nullptr, 0);
|
||||
if (ConfLen > 0) {
|
||||
do {
|
||||
Result.resize(ConfLen);
|
||||
ConfLen = confstr(ConfName, Result.data(), Result.size());
|
||||
} while (ConfLen > 0 && ConfLen != Result.size());
|
||||
|
||||
if (ConfLen > 0) {
|
||||
assert(Result.back() == 0);
|
||||
Result.pop_back();
|
||||
return true;
|
||||
}
|
||||
|
||||
Result.clear();
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool getUserCacheDir(SmallVectorImpl<char> &Result) {
|
||||
// First try using XDG_CACHE_HOME env variable,
|
||||
// as specified in XDG Base Directory Specification at
|
||||
// http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||
if (const char *XdgCacheDir = std::getenv("XDG_CACHE_HOME")) {
|
||||
Result.clear();
|
||||
Result.append(XdgCacheDir, XdgCacheDir + strlen(XdgCacheDir));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Try Darwin configuration query
|
||||
if (getDarwinConfDir(false, Result))
|
||||
return true;
|
||||
|
||||
// Use "$HOME/.cache" if $HOME is available
|
||||
if (home_directory(Result)) {
|
||||
append(Result, ".cache");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char *getEnvTempDir() {
|
||||
// Check whether the temporary directory is specified by an environment
|
||||
// variable.
|
||||
const char *EnvironmentVariables[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"};
|
||||
for (const char *Env : EnvironmentVariables) {
|
||||
if (const char *Dir = std::getenv(Env))
|
||||
return Dir;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static const char *getDefaultTempDir(bool ErasedOnReboot) {
|
||||
#ifdef P_tmpdir
|
||||
if ((bool)P_tmpdir)
|
||||
return P_tmpdir;
|
||||
#endif
|
||||
|
||||
if (ErasedOnReboot)
|
||||
return "/tmp";
|
||||
return "/var/tmp";
|
||||
}
|
||||
|
||||
void system_temp_directory(bool ErasedOnReboot, SmallVectorImpl<char> &Result) {
|
||||
Result.clear();
|
||||
|
||||
if (ErasedOnReboot) {
|
||||
// There is no env variable for the cache directory.
|
||||
if (const char *RequestedDir = getEnvTempDir()) {
|
||||
Result.append(RequestedDir, RequestedDir + strlen(RequestedDir));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (getDarwinConfDir(ErasedOnReboot, Result))
|
||||
return;
|
||||
|
||||
const char *RequestedDir = getDefaultTempDir(ErasedOnReboot);
|
||||
Result.append(RequestedDir, RequestedDir + strlen(RequestedDir));
|
||||
}
|
||||
|
||||
} // end namespace path
|
||||
} // end namespace sys
|
||||
} // end namespace llvm
|
||||
648
wpiutil/src/main/native/cpp/llvm/Windows/Path.inc
Normal file
648
wpiutil/src/main/native/cpp/llvm/Windows/Path.inc
Normal file
@@ -0,0 +1,648 @@
|
||||
//===- llvm/Support/Windows/Path.inc - Windows Path Impl --------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements the Windows specific implementation of the Path API.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
//=== WARNING: Implementation here must contain only generic Windows code that
|
||||
//=== is guaranteed to work on *all* Windows variants.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/STLExtras.h"
|
||||
#include "llvm/WindowsError.h"
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
// These two headers must be included last, and make sure shlobj is required
|
||||
// after Windows.h to make sure it picks up our definition of _WIN32_WINNT
|
||||
#include "WindowsSupport.h"
|
||||
#include <shlobj.h>
|
||||
|
||||
#undef max
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma comment(lib, "shell32.lib")
|
||||
# pragma comment(lib, "ole32.lib")
|
||||
#endif
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
using llvm::sys::windows::UTF8ToUTF16;
|
||||
using llvm::sys::windows::UTF16ToUTF8;
|
||||
using llvm::sys::path::widenPath;
|
||||
|
||||
static bool is_separator(const wchar_t value) {
|
||||
switch (value) {
|
||||
case L'\\':
|
||||
case L'/':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
namespace llvm {
|
||||
namespace sys {
|
||||
namespace path {
|
||||
|
||||
// Convert a UTF-8 path to UTF-16. Also, if the absolute equivalent of the
|
||||
// path is longer than CreateDirectory can tolerate, make it absolute and
|
||||
// prefixed by '\\?\'.
|
||||
std::error_code widenPath(const Twine &Path8,
|
||||
SmallVectorImpl<wchar_t> &Path16) {
|
||||
const size_t MaxDirLen = MAX_PATH - 12; // Must leave room for 8.3 filename.
|
||||
|
||||
// Several operations would convert Path8 to SmallString; more efficient to
|
||||
// do it once up front.
|
||||
SmallString<128> Path8Str;
|
||||
Path8.toVector(Path8Str);
|
||||
|
||||
// If we made this path absolute, how much longer would it get?
|
||||
size_t CurPathLen;
|
||||
if (llvm::sys::path::is_absolute(Twine(Path8Str)))
|
||||
CurPathLen = 0; // No contribution from current_path needed.
|
||||
else {
|
||||
CurPathLen = ::GetCurrentDirectoryW(0, NULL);
|
||||
if (CurPathLen == 0)
|
||||
return mapWindowsError(::GetLastError());
|
||||
}
|
||||
|
||||
// Would the absolute path be longer than our limit?
|
||||
if ((Path8Str.size() + CurPathLen) >= MaxDirLen &&
|
||||
!Path8Str.startswith("\\\\?\\")) {
|
||||
SmallString<2*MAX_PATH> FullPath("\\\\?\\");
|
||||
if (CurPathLen) {
|
||||
SmallString<80> CurPath;
|
||||
if (std::error_code EC = llvm::sys::fs::current_path(CurPath))
|
||||
return EC;
|
||||
FullPath.append(CurPath);
|
||||
}
|
||||
// Traverse the requested path, canonicalizing . and .. as we go (because
|
||||
// the \\?\ prefix is documented to treat them as real components).
|
||||
// The iterators don't report separators and append() always attaches
|
||||
// preferred_separator so we don't need to call native() on the result.
|
||||
for (llvm::sys::path::const_iterator I = llvm::sys::path::begin(Path8Str),
|
||||
E = llvm::sys::path::end(Path8Str);
|
||||
I != E; ++I) {
|
||||
if (I->size() == 1 && *I == ".")
|
||||
continue;
|
||||
if (I->size() == 2 && *I == "..")
|
||||
llvm::sys::path::remove_filename(FullPath);
|
||||
else
|
||||
llvm::sys::path::append(FullPath, *I);
|
||||
}
|
||||
return UTF8ToUTF16(FullPath, Path16);
|
||||
}
|
||||
|
||||
// Just use the caller's original path.
|
||||
return UTF8ToUTF16(Path8Str, Path16);
|
||||
}
|
||||
} // end namespace path
|
||||
|
||||
namespace fs {
|
||||
|
||||
UniqueID file_status::getUniqueID() const {
|
||||
// The file is uniquely identified by the volume serial number along
|
||||
// with the 64-bit file identifier.
|
||||
uint64_t FileID = (static_cast<uint64_t>(FileIndexHigh) << 32ULL) |
|
||||
static_cast<uint64_t>(FileIndexLow);
|
||||
|
||||
return UniqueID(VolumeSerialNumber, FileID);
|
||||
}
|
||||
|
||||
std::error_code current_path(SmallVectorImpl<char> &result) {
|
||||
SmallVector<wchar_t, MAX_PATH> cur_path;
|
||||
DWORD len = MAX_PATH;
|
||||
|
||||
do {
|
||||
cur_path.reserve(len);
|
||||
len = ::GetCurrentDirectoryW(cur_path.capacity(), cur_path.data());
|
||||
|
||||
// A zero return value indicates a failure other than insufficient space.
|
||||
if (len == 0)
|
||||
return mapWindowsError(::GetLastError());
|
||||
|
||||
// If there's insufficient space, the len returned is larger than the len
|
||||
// given.
|
||||
} while (len > cur_path.capacity());
|
||||
|
||||
// On success, GetCurrentDirectoryW returns the number of characters not
|
||||
// including the null-terminator.
|
||||
cur_path.set_size(len);
|
||||
return UTF16ToUTF8(cur_path.begin(), cur_path.size(), result);
|
||||
}
|
||||
|
||||
|
||||
std::error_code access(const Twine &Path, AccessMode Mode) {
|
||||
SmallVector<wchar_t, 128> PathUtf16;
|
||||
|
||||
if (std::error_code EC = widenPath(Path, PathUtf16))
|
||||
return EC;
|
||||
|
||||
DWORD Attributes = ::GetFileAttributesW(PathUtf16.begin());
|
||||
|
||||
if (Attributes == INVALID_FILE_ATTRIBUTES) {
|
||||
// See if the file didn't actually exist.
|
||||
DWORD LastError = ::GetLastError();
|
||||
if (LastError != ERROR_FILE_NOT_FOUND &&
|
||||
LastError != ERROR_PATH_NOT_FOUND)
|
||||
return mapWindowsError(LastError);
|
||||
return std::make_error_code(std::errc::no_such_file_or_directory);
|
||||
}
|
||||
|
||||
if (Mode == AccessMode::Write && (Attributes & FILE_ATTRIBUTE_READONLY))
|
||||
return std::make_error_code(std::errc::permission_denied);
|
||||
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
bool equivalent(file_status A, file_status B) {
|
||||
assert(status_known(A) && status_known(B));
|
||||
return A.FileIndexHigh == B.FileIndexHigh &&
|
||||
A.FileIndexLow == B.FileIndexLow &&
|
||||
A.FileSizeHigh == B.FileSizeHigh &&
|
||||
A.FileSizeLow == B.FileSizeLow &&
|
||||
A.LastAccessedTimeHigh == B.LastAccessedTimeHigh &&
|
||||
A.LastAccessedTimeLow == B.LastAccessedTimeLow &&
|
||||
A.LastWriteTimeHigh == B.LastWriteTimeHigh &&
|
||||
A.LastWriteTimeLow == B.LastWriteTimeLow &&
|
||||
A.VolumeSerialNumber == B.VolumeSerialNumber;
|
||||
}
|
||||
|
||||
std::error_code equivalent(const Twine &A, const Twine &B, bool &result) {
|
||||
file_status fsA, fsB;
|
||||
if (std::error_code ec = status(A, fsA))
|
||||
return ec;
|
||||
if (std::error_code ec = status(B, fsB))
|
||||
return ec;
|
||||
result = equivalent(fsA, fsB);
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
static bool isReservedName(StringRef path) {
|
||||
// This list of reserved names comes from MSDN, at:
|
||||
// http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx
|
||||
static const char *const sReservedNames[] = { "nul", "con", "prn", "aux",
|
||||
"com1", "com2", "com3", "com4",
|
||||
"com5", "com6", "com7", "com8",
|
||||
"com9", "lpt1", "lpt2", "lpt3",
|
||||
"lpt4", "lpt5", "lpt6", "lpt7",
|
||||
"lpt8", "lpt9" };
|
||||
|
||||
// First, check to see if this is a device namespace, which always
|
||||
// starts with \\.\, since device namespaces are not legal file paths.
|
||||
if (path.startswith("\\\\.\\"))
|
||||
return true;
|
||||
|
||||
// Then compare against the list of ancient reserved names.
|
||||
for (size_t i = 0; i < array_lengthof(sReservedNames); ++i) {
|
||||
if (path.equals_lower(sReservedNames[i]))
|
||||
return true;
|
||||
}
|
||||
|
||||
// The path isn't what we consider reserved.
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::error_code getStatus(HANDLE FileHandle, file_status &Result) {
|
||||
if (FileHandle == INVALID_HANDLE_VALUE)
|
||||
goto handle_status_error;
|
||||
|
||||
switch (::GetFileType(FileHandle)) {
|
||||
default:
|
||||
Result = file_status(file_type::type_unknown);
|
||||
return std::error_code();
|
||||
case FILE_TYPE_UNKNOWN: {
|
||||
DWORD Err = ::GetLastError();
|
||||
if (Err != NO_ERROR)
|
||||
return mapWindowsError(Err);
|
||||
Result = file_status(file_type::type_unknown);
|
||||
return std::error_code();
|
||||
}
|
||||
case FILE_TYPE_DISK:
|
||||
break;
|
||||
case FILE_TYPE_CHAR:
|
||||
Result = file_status(file_type::character_file);
|
||||
return std::error_code();
|
||||
case FILE_TYPE_PIPE:
|
||||
Result = file_status(file_type::fifo_file);
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
BY_HANDLE_FILE_INFORMATION Info;
|
||||
if (!::GetFileInformationByHandle(FileHandle, &Info))
|
||||
goto handle_status_error;
|
||||
|
||||
{
|
||||
file_type Type = (Info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
? file_type::directory_file
|
||||
: file_type::regular_file;
|
||||
Result =
|
||||
file_status(Type, Info.ftLastAccessTime.dwHighDateTime,
|
||||
Info.ftLastAccessTime.dwLowDateTime,
|
||||
Info.ftLastWriteTime.dwHighDateTime,
|
||||
Info.ftLastWriteTime.dwLowDateTime,
|
||||
Info.dwVolumeSerialNumber, Info.nFileSizeHigh,
|
||||
Info.nFileSizeLow, Info.nFileIndexHigh, Info.nFileIndexLow);
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
handle_status_error:
|
||||
DWORD LastError = ::GetLastError();
|
||||
if (LastError == ERROR_FILE_NOT_FOUND ||
|
||||
LastError == ERROR_PATH_NOT_FOUND)
|
||||
Result = file_status(file_type::file_not_found);
|
||||
else if (LastError == ERROR_SHARING_VIOLATION)
|
||||
Result = file_status(file_type::type_unknown);
|
||||
else
|
||||
Result = file_status(file_type::status_error);
|
||||
return mapWindowsError(LastError);
|
||||
}
|
||||
|
||||
std::error_code status(const Twine &path, file_status &result) {
|
||||
SmallString<128> path_storage;
|
||||
SmallVector<wchar_t, 128> path_utf16;
|
||||
|
||||
StringRef path8 = path.toStringRef(path_storage);
|
||||
if (isReservedName(path8)) {
|
||||
result = file_status(file_type::character_file);
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
if (std::error_code ec = widenPath(path8, path_utf16))
|
||||
return ec;
|
||||
|
||||
DWORD attr = ::GetFileAttributesW(path_utf16.begin());
|
||||
if (attr == INVALID_FILE_ATTRIBUTES)
|
||||
return getStatus(INVALID_HANDLE_VALUE, result);
|
||||
|
||||
// Handle reparse points.
|
||||
if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
|
||||
ScopedFileHandle h(
|
||||
::CreateFileW(path_utf16.begin(),
|
||||
0, // Attributes only.
|
||||
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS,
|
||||
0));
|
||||
if (!h)
|
||||
return getStatus(INVALID_HANDLE_VALUE, result);
|
||||
}
|
||||
|
||||
ScopedFileHandle h(
|
||||
::CreateFileW(path_utf16.begin(), 0, // Attributes only.
|
||||
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0));
|
||||
if (!h)
|
||||
return getStatus(INVALID_HANDLE_VALUE, result);
|
||||
|
||||
return getStatus(h, result);
|
||||
}
|
||||
|
||||
std::error_code status(int FD, file_status &Result) {
|
||||
HANDLE FileHandle = reinterpret_cast<HANDLE>(_get_osfhandle(FD));
|
||||
return getStatus(FileHandle, Result);
|
||||
}
|
||||
|
||||
std::error_code detail::directory_iterator_construct(detail::DirIterState &it,
|
||||
StringRef path){
|
||||
SmallVector<wchar_t, 128> path_utf16;
|
||||
|
||||
if (std::error_code ec = widenPath(path, path_utf16))
|
||||
return ec;
|
||||
|
||||
// Convert path to the format that Windows is happy with.
|
||||
if (path_utf16.size() > 0 &&
|
||||
!is_separator(path_utf16[path.size() - 1]) &&
|
||||
path_utf16[path.size() - 1] != L':') {
|
||||
path_utf16.push_back(L'\\');
|
||||
path_utf16.push_back(L'*');
|
||||
} else {
|
||||
path_utf16.push_back(L'*');
|
||||
}
|
||||
|
||||
// Get the first directory entry.
|
||||
WIN32_FIND_DATAW FirstFind;
|
||||
ScopedFindHandle FindHandle(::FindFirstFileW(c_str(path_utf16), &FirstFind));
|
||||
if (!FindHandle)
|
||||
return mapWindowsError(::GetLastError());
|
||||
|
||||
size_t FilenameLen = ::wcslen(FirstFind.cFileName);
|
||||
while ((FilenameLen == 1 && FirstFind.cFileName[0] == L'.') ||
|
||||
(FilenameLen == 2 && FirstFind.cFileName[0] == L'.' &&
|
||||
FirstFind.cFileName[1] == L'.'))
|
||||
if (!::FindNextFileW(FindHandle, &FirstFind)) {
|
||||
DWORD LastError = ::GetLastError();
|
||||
// Check for end.
|
||||
if (LastError == ERROR_NO_MORE_FILES)
|
||||
return detail::directory_iterator_destruct(it);
|
||||
return mapWindowsError(LastError);
|
||||
} else
|
||||
FilenameLen = ::wcslen(FirstFind.cFileName);
|
||||
|
||||
// Construct the current directory entry.
|
||||
SmallString<128> directory_entry_name_utf8;
|
||||
if (std::error_code ec =
|
||||
UTF16ToUTF8(FirstFind.cFileName, ::wcslen(FirstFind.cFileName),
|
||||
directory_entry_name_utf8))
|
||||
return ec;
|
||||
|
||||
it.IterationHandle = intptr_t(FindHandle.take());
|
||||
SmallString<128> directory_entry_path(path);
|
||||
path::append(directory_entry_path, directory_entry_name_utf8);
|
||||
it.CurrentEntry = directory_entry(directory_entry_path);
|
||||
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
std::error_code detail::directory_iterator_destruct(detail::DirIterState &it) {
|
||||
if (it.IterationHandle != 0)
|
||||
// Closes the handle if it's valid.
|
||||
ScopedFindHandle close(HANDLE(it.IterationHandle));
|
||||
it.IterationHandle = 0;
|
||||
it.CurrentEntry = directory_entry();
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
std::error_code detail::directory_iterator_increment(detail::DirIterState &it) {
|
||||
WIN32_FIND_DATAW FindData;
|
||||
if (!::FindNextFileW(HANDLE(it.IterationHandle), &FindData)) {
|
||||
DWORD LastError = ::GetLastError();
|
||||
// Check for end.
|
||||
if (LastError == ERROR_NO_MORE_FILES)
|
||||
return detail::directory_iterator_destruct(it);
|
||||
return mapWindowsError(LastError);
|
||||
}
|
||||
|
||||
size_t FilenameLen = ::wcslen(FindData.cFileName);
|
||||
if ((FilenameLen == 1 && FindData.cFileName[0] == L'.') ||
|
||||
(FilenameLen == 2 && FindData.cFileName[0] == L'.' &&
|
||||
FindData.cFileName[1] == L'.'))
|
||||
return directory_iterator_increment(it);
|
||||
|
||||
SmallString<128> directory_entry_path_utf8;
|
||||
if (std::error_code ec =
|
||||
UTF16ToUTF8(FindData.cFileName, ::wcslen(FindData.cFileName),
|
||||
directory_entry_path_utf8))
|
||||
return ec;
|
||||
|
||||
it.CurrentEntry.replace_filename(Twine(directory_entry_path_utf8));
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
std::error_code openFileForRead(const Twine &Name, int &ResultFD,
|
||||
SmallVectorImpl<char> *RealPath) {
|
||||
SmallVector<wchar_t, 128> PathUTF16;
|
||||
|
||||
if (std::error_code EC = widenPath(Name, PathUTF16))
|
||||
return EC;
|
||||
|
||||
HANDLE H =
|
||||
::CreateFileW(PathUTF16.begin(), GENERIC_READ,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (H == INVALID_HANDLE_VALUE) {
|
||||
DWORD LastError = ::GetLastError();
|
||||
std::error_code EC = mapWindowsError(LastError);
|
||||
// Provide a better error message when trying to open directories.
|
||||
// This only runs if we failed to open the file, so there is probably
|
||||
// no performances issues.
|
||||
if (LastError != ERROR_ACCESS_DENIED)
|
||||
return EC;
|
||||
if (is_directory(Name))
|
||||
return std::make_error_code(std::errc::is_a_directory);
|
||||
return EC;
|
||||
}
|
||||
|
||||
int FD = ::_open_osfhandle(intptr_t(H), 0);
|
||||
if (FD == -1) {
|
||||
::CloseHandle(H);
|
||||
return mapWindowsError(ERROR_INVALID_HANDLE);
|
||||
}
|
||||
|
||||
// Fetch the real name of the file, if the user asked
|
||||
if (RealPath) {
|
||||
RealPath->clear();
|
||||
wchar_t RealPathUTF16[MAX_PATH];
|
||||
DWORD CountChars =
|
||||
::GetFinalPathNameByHandleW(H, RealPathUTF16, MAX_PATH,
|
||||
FILE_NAME_NORMALIZED);
|
||||
if (CountChars > 0 && CountChars < MAX_PATH) {
|
||||
// Convert the result from UTF-16 to UTF-8.
|
||||
SmallString<MAX_PATH> RealPathUTF8;
|
||||
if (!UTF16ToUTF8(RealPathUTF16, CountChars, RealPathUTF8))
|
||||
RealPath->append(RealPathUTF8.data(),
|
||||
RealPathUTF8.data() + strlen(RealPathUTF8.data()));
|
||||
}
|
||||
}
|
||||
|
||||
ResultFD = FD;
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
std::error_code openFileForWrite(const Twine &Name, int &ResultFD,
|
||||
OpenFlags Flags, unsigned Mode) {
|
||||
// Verify that we don't have both "append" and "excl".
|
||||
assert((!(Flags & F_Excl) || !(Flags & F_Append)) &&
|
||||
"Cannot specify both 'excl' and 'append' file creation flags!");
|
||||
|
||||
SmallVector<wchar_t, 128> PathUTF16;
|
||||
|
||||
if (std::error_code EC = widenPath(Name, PathUTF16))
|
||||
return EC;
|
||||
|
||||
DWORD CreationDisposition;
|
||||
if (Flags & F_Excl)
|
||||
CreationDisposition = CREATE_NEW;
|
||||
else if (Flags & F_Append)
|
||||
CreationDisposition = OPEN_ALWAYS;
|
||||
else
|
||||
CreationDisposition = CREATE_ALWAYS;
|
||||
|
||||
DWORD Access = GENERIC_WRITE;
|
||||
if (Flags & F_RW)
|
||||
Access |= GENERIC_READ;
|
||||
|
||||
HANDLE H = ::CreateFileW(PathUTF16.begin(), Access,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
|
||||
CreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
if (H == INVALID_HANDLE_VALUE) {
|
||||
DWORD LastError = ::GetLastError();
|
||||
std::error_code EC = mapWindowsError(LastError);
|
||||
// Provide a better error message when trying to open directories.
|
||||
// This only runs if we failed to open the file, so there is probably
|
||||
// no performances issues.
|
||||
if (LastError != ERROR_ACCESS_DENIED)
|
||||
return EC;
|
||||
if (is_directory(Name))
|
||||
return std::make_error_code(std::errc::is_a_directory);
|
||||
return EC;
|
||||
}
|
||||
|
||||
int OpenFlags = 0;
|
||||
if (Flags & F_Append)
|
||||
OpenFlags |= _O_APPEND;
|
||||
|
||||
if (Flags & F_Text)
|
||||
OpenFlags |= _O_TEXT;
|
||||
|
||||
int FD = ::_open_osfhandle(intptr_t(H), OpenFlags);
|
||||
if (FD == -1) {
|
||||
::CloseHandle(H);
|
||||
return mapWindowsError(ERROR_INVALID_HANDLE);
|
||||
}
|
||||
|
||||
ResultFD = FD;
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
} // end namespace fs
|
||||
|
||||
namespace path {
|
||||
static bool getKnownFolderPath(KNOWNFOLDERID folderId,
|
||||
SmallVectorImpl<char> &result) {
|
||||
wchar_t *path = nullptr;
|
||||
if (::SHGetKnownFolderPath(folderId, KF_FLAG_CREATE, nullptr, &path) != S_OK)
|
||||
return false;
|
||||
|
||||
bool ok = !UTF16ToUTF8(path, ::wcslen(path), result);
|
||||
::CoTaskMemFree(path);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool getUserCacheDir(SmallVectorImpl<char> &Result) {
|
||||
return getKnownFolderPath(FOLDERID_LocalAppData, Result);
|
||||
}
|
||||
|
||||
bool home_directory(SmallVectorImpl<char> &result) {
|
||||
return getKnownFolderPath(FOLDERID_Profile, result);
|
||||
}
|
||||
|
||||
static bool getTempDirEnvVar(const wchar_t *Var, SmallVectorImpl<char> &Res) {
|
||||
SmallVector<wchar_t, 1024> Buf;
|
||||
size_t Size = 1024;
|
||||
do {
|
||||
Buf.reserve(Size);
|
||||
Size = GetEnvironmentVariableW(Var, Buf.data(), Buf.capacity());
|
||||
if (Size == 0)
|
||||
return false;
|
||||
|
||||
// Try again with larger buffer.
|
||||
} while (Size > Buf.capacity());
|
||||
Buf.set_size(Size);
|
||||
|
||||
return !windows::UTF16ToUTF8(Buf.data(), Size, Res);
|
||||
}
|
||||
|
||||
static bool getTempDirEnvVar(SmallVectorImpl<char> &Res) {
|
||||
const wchar_t *EnvironmentVariables[] = {L"TMP", L"TEMP", L"USERPROFILE"};
|
||||
for (auto *Env : EnvironmentVariables) {
|
||||
if (getTempDirEnvVar(Env, Res))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void system_temp_directory(bool ErasedOnReboot, SmallVectorImpl<char> &Result) {
|
||||
(void)ErasedOnReboot;
|
||||
Result.clear();
|
||||
|
||||
// Check whether the temporary directory is specified by an environment var.
|
||||
// This matches GetTempPath logic to some degree. GetTempPath is not used
|
||||
// directly as it cannot handle evn var longer than 130 chars on Windows 7
|
||||
// (fixed on Windows 8).
|
||||
if (getTempDirEnvVar(Result)) {
|
||||
assert(!Result.empty() && "Unexpected empty path");
|
||||
native(Result); // Some Unix-like shells use Unix path separator in $TMP.
|
||||
fs::make_absolute(Result); // Make it absolute if not already.
|
||||
return;
|
||||
}
|
||||
|
||||
// Fall back to a system default.
|
||||
const char *DefaultResult = "C:\\Temp";
|
||||
Result.append(DefaultResult, DefaultResult + strlen(DefaultResult));
|
||||
}
|
||||
} // end namespace path
|
||||
|
||||
namespace windows {
|
||||
std::error_code UTF8ToUTF16(llvm::StringRef utf8,
|
||||
llvm::SmallVectorImpl<wchar_t> &utf16) {
|
||||
if (!utf8.empty()) {
|
||||
int len = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8.begin(),
|
||||
utf8.size(), utf16.begin(), 0);
|
||||
|
||||
if (len == 0)
|
||||
return mapWindowsError(::GetLastError());
|
||||
|
||||
utf16.reserve(len + 1);
|
||||
utf16.set_size(len);
|
||||
|
||||
len = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8.begin(),
|
||||
utf8.size(), utf16.begin(), utf16.size());
|
||||
|
||||
if (len == 0)
|
||||
return mapWindowsError(::GetLastError());
|
||||
}
|
||||
|
||||
// Make utf16 null terminated.
|
||||
utf16.push_back(0);
|
||||
utf16.pop_back();
|
||||
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
static
|
||||
std::error_code UTF16ToCodePage(unsigned codepage, const wchar_t *utf16,
|
||||
size_t utf16_len,
|
||||
llvm::SmallVectorImpl<char> &utf8) {
|
||||
if (utf16_len) {
|
||||
// Get length.
|
||||
int len = ::WideCharToMultiByte(codepage, 0, utf16, utf16_len, utf8.begin(),
|
||||
0, NULL, NULL);
|
||||
|
||||
if (len == 0)
|
||||
return mapWindowsError(::GetLastError());
|
||||
|
||||
utf8.reserve(len);
|
||||
utf8.set_size(len);
|
||||
|
||||
// Now do the actual conversion.
|
||||
len = ::WideCharToMultiByte(codepage, 0, utf16, utf16_len, utf8.data(),
|
||||
utf8.size(), NULL, NULL);
|
||||
|
||||
if (len == 0)
|
||||
return mapWindowsError(::GetLastError());
|
||||
}
|
||||
|
||||
// Make utf8 null terminated.
|
||||
utf8.push_back(0);
|
||||
utf8.pop_back();
|
||||
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
std::error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len,
|
||||
llvm::SmallVectorImpl<char> &utf8) {
|
||||
return UTF16ToCodePage(CP_UTF8, utf16, utf16_len, utf8);
|
||||
}
|
||||
|
||||
std::error_code UTF16ToCurCP(const wchar_t *utf16, size_t utf16_len,
|
||||
llvm::SmallVectorImpl<char> &utf8) {
|
||||
return UTF16ToCodePage(CP_ACP, utf16, utf16_len, utf8);
|
||||
}
|
||||
|
||||
} // end namespace windows
|
||||
} // end namespace sys
|
||||
} // end namespace llvm
|
||||
211
wpiutil/src/main/native/cpp/llvm/Windows/WindowsSupport.h
Normal file
211
wpiutil/src/main/native/cpp/llvm/Windows/WindowsSupport.h
Normal file
@@ -0,0 +1,211 @@
|
||||
//===- WindowsSupport.h - Common Windows Include File -----------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines things specific to Windows implementations. In addition to
|
||||
// providing some helpers for working with win32 APIs, this header wraps
|
||||
// <windows.h> with some portability macros. Always include WindowsSupport.h
|
||||
// instead of including <windows.h> directly.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
//=== WARNING: Implementation here must contain only generic Win32 code that
|
||||
//=== is guaranteed to work on *all* Win32 variants.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_SUPPORT_WINDOWSSUPPORT_H
|
||||
#define LLVM_SUPPORT_WINDOWSSUPPORT_H
|
||||
|
||||
// mingw-w64 tends to define it as 0x0502 in its headers.
|
||||
#undef _WIN32_WINNT
|
||||
#undef _WIN32_IE
|
||||
|
||||
// Require at least Windows 7 API.
|
||||
#define _WIN32_WINNT 0x0601
|
||||
#define _WIN32_IE 0x0800 // MinGW at it again. FIXME: verify if still needed.
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include "llvm/SmallVector.h"
|
||||
#include "llvm/StringExtras.h"
|
||||
#include "llvm/StringRef.h"
|
||||
#include "llvm/Twine.h"
|
||||
#include "llvm/Compiler.h"
|
||||
#include <system_error>
|
||||
#include <windows.h>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
|
||||
/// Determines if the program is running on Windows 8 or newer. This
|
||||
/// reimplements one of the helpers in the Windows 8.1 SDK, which are intended
|
||||
/// to supercede raw calls to GetVersionEx. Old SDKs, Cygwin, and MinGW don't
|
||||
/// yet have VersionHelpers.h, so we have our own helper.
|
||||
inline bool RunningWindows8OrGreater() {
|
||||
// Windows 8 is version 6.2, service pack 0.
|
||||
OSVERSIONINFOEXW osvi = {};
|
||||
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
||||
osvi.dwMajorVersion = 6;
|
||||
osvi.dwMinorVersion = 2;
|
||||
osvi.wServicePackMajor = 0;
|
||||
|
||||
DWORDLONG Mask = 0;
|
||||
Mask = VerSetConditionMask(Mask, VER_MAJORVERSION, VER_GREATER_EQUAL);
|
||||
Mask = VerSetConditionMask(Mask, VER_MINORVERSION, VER_GREATER_EQUAL);
|
||||
Mask = VerSetConditionMask(Mask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
|
||||
|
||||
return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION |
|
||||
VER_SERVICEPACKMAJOR,
|
||||
Mask) != FALSE;
|
||||
}
|
||||
|
||||
inline bool MakeErrMsg(std::string *ErrMsg, const std::string &prefix) {
|
||||
if (!ErrMsg)
|
||||
return true;
|
||||
char *buffer = NULL;
|
||||
DWORD LastError = GetLastError();
|
||||
DWORD R = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_MAX_WIDTH_MASK,
|
||||
NULL, LastError, 0, (LPSTR)&buffer, 1, NULL);
|
||||
if (R)
|
||||
*ErrMsg = prefix + ": " + buffer;
|
||||
else
|
||||
*ErrMsg = prefix + ": Unknown error";
|
||||
*ErrMsg += " (0x" + llvm::utohexstr(LastError) + ")";
|
||||
|
||||
LocalFree(buffer);
|
||||
return R != 0;
|
||||
}
|
||||
|
||||
template <typename HandleTraits>
|
||||
class ScopedHandle {
|
||||
typedef typename HandleTraits::handle_type handle_type;
|
||||
handle_type Handle;
|
||||
|
||||
ScopedHandle(const ScopedHandle &other); // = delete;
|
||||
void operator=(const ScopedHandle &other); // = delete;
|
||||
public:
|
||||
ScopedHandle()
|
||||
: Handle(HandleTraits::GetInvalid()) {}
|
||||
|
||||
explicit ScopedHandle(handle_type h)
|
||||
: Handle(h) {}
|
||||
|
||||
~ScopedHandle() {
|
||||
if (HandleTraits::IsValid(Handle))
|
||||
HandleTraits::Close(Handle);
|
||||
}
|
||||
|
||||
handle_type take() {
|
||||
handle_type t = Handle;
|
||||
Handle = HandleTraits::GetInvalid();
|
||||
return t;
|
||||
}
|
||||
|
||||
ScopedHandle &operator=(handle_type h) {
|
||||
if (HandleTraits::IsValid(Handle))
|
||||
HandleTraits::Close(Handle);
|
||||
Handle = h;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// True if Handle is valid.
|
||||
explicit operator bool() const {
|
||||
return HandleTraits::IsValid(Handle) ? true : false;
|
||||
}
|
||||
|
||||
operator handle_type() const {
|
||||
return Handle;
|
||||
}
|
||||
};
|
||||
|
||||
struct CommonHandleTraits {
|
||||
typedef HANDLE handle_type;
|
||||
|
||||
static handle_type GetInvalid() {
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
static void Close(handle_type h) {
|
||||
::CloseHandle(h);
|
||||
}
|
||||
|
||||
static bool IsValid(handle_type h) {
|
||||
return h != GetInvalid();
|
||||
}
|
||||
};
|
||||
|
||||
struct JobHandleTraits : CommonHandleTraits {
|
||||
static handle_type GetInvalid() {
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
|
||||
struct RegTraits : CommonHandleTraits {
|
||||
typedef HKEY handle_type;
|
||||
|
||||
static handle_type GetInvalid() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void Close(handle_type h) {
|
||||
::RegCloseKey(h);
|
||||
}
|
||||
|
||||
static bool IsValid(handle_type h) {
|
||||
return h != GetInvalid();
|
||||
}
|
||||
};
|
||||
|
||||
struct FindHandleTraits : CommonHandleTraits {
|
||||
static void Close(handle_type h) {
|
||||
::FindClose(h);
|
||||
}
|
||||
};
|
||||
|
||||
struct FileHandleTraits : CommonHandleTraits {};
|
||||
|
||||
typedef ScopedHandle<CommonHandleTraits> ScopedCommonHandle;
|
||||
typedef ScopedHandle<FileHandleTraits> ScopedFileHandle;
|
||||
typedef ScopedHandle<RegTraits> ScopedRegHandle;
|
||||
typedef ScopedHandle<FindHandleTraits> ScopedFindHandle;
|
||||
typedef ScopedHandle<JobHandleTraits> ScopedJobHandle;
|
||||
|
||||
namespace llvm {
|
||||
template <class T>
|
||||
class SmallVectorImpl;
|
||||
|
||||
template <class T>
|
||||
typename SmallVectorImpl<T>::const_pointer
|
||||
c_str(SmallVectorImpl<T> &str) {
|
||||
str.push_back(0);
|
||||
str.pop_back();
|
||||
return str.data();
|
||||
}
|
||||
|
||||
namespace sys {
|
||||
namespace path {
|
||||
std::error_code widenPath(const Twine &Path8,
|
||||
SmallVectorImpl<wchar_t> &Path16);
|
||||
} // end namespace path
|
||||
|
||||
namespace windows {
|
||||
std::error_code UTF8ToUTF16(StringRef utf8, SmallVectorImpl<wchar_t> &utf16);
|
||||
std::error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len,
|
||||
SmallVectorImpl<char> &utf8);
|
||||
/// Convert from UTF16 to the current code page used in the system
|
||||
std::error_code UTF16ToCurCP(const wchar_t *utf16, size_t utf16_len,
|
||||
SmallVectorImpl<char> &utf8);
|
||||
} // end namespace windows
|
||||
} // end namespace sys
|
||||
} // end namespace llvm.
|
||||
|
||||
#endif
|
||||
30
wpiutil/src/main/native/cpp/llvm/raw_os_ostream.cpp
Normal file
30
wpiutil/src/main/native/cpp/llvm/raw_os_ostream.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
//===--- raw_os_ostream.cpp - Implement the raw_os_ostream class ----------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This implements support adapting raw_ostream to std::ostream.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/raw_os_ostream.h"
|
||||
#include <ostream>
|
||||
using namespace llvm;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// raw_os_ostream
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
raw_os_ostream::~raw_os_ostream() {
|
||||
flush();
|
||||
}
|
||||
|
||||
void raw_os_ostream::write_impl(const char *Ptr, size_t Size) {
|
||||
OS.write(Ptr, Size);
|
||||
}
|
||||
|
||||
uint64_t raw_os_ostream::current_pos() const { return OS.tellp(); }
|
||||
733
wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp
Normal file
733
wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp
Normal file
@@ -0,0 +1,733 @@
|
||||
//===--- raw_ostream.cpp - Implement the raw_ostream classes --------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This implements support for bulk buffered stream output.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/raw_ostream.h"
|
||||
#include "llvm/SmallString.h"
|
||||
#include "llvm/SmallVector.h"
|
||||
#include "llvm/StringExtras.h"
|
||||
#include "llvm/Compiler.h"
|
||||
#include "llvm/Format.h"
|
||||
#include "llvm/MathExtras.h"
|
||||
#include "llvm/WindowsError.h"
|
||||
#include <cctype>
|
||||
#include <cerrno>
|
||||
#include <sys/stat.h>
|
||||
#include <system_error>
|
||||
|
||||
// <fcntl.h> may provide O_BINARY.
|
||||
#include <fcntl.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#include <sys/uio.h>
|
||||
#endif
|
||||
|
||||
#if defined(__CYGWIN__)
|
||||
#include <io.h>
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <io.h>
|
||||
#ifndef STDIN_FILENO
|
||||
# define STDIN_FILENO 0
|
||||
#endif
|
||||
#ifndef STDOUT_FILENO
|
||||
# define STDOUT_FILENO 1
|
||||
#endif
|
||||
#ifndef STDERR_FILENO
|
||||
# define STDERR_FILENO 2
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include "Windows/WindowsSupport.h"
|
||||
#endif
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
raw_ostream::~raw_ostream() {
|
||||
// raw_ostream's subclasses should take care to flush the buffer
|
||||
// in their destructors.
|
||||
assert(OutBufCur == OutBufStart &&
|
||||
"raw_ostream destructor called with non-empty buffer!");
|
||||
|
||||
if (BufferMode == InternalBuffer)
|
||||
delete [] OutBufStart;
|
||||
}
|
||||
|
||||
// An out of line virtual method to provide a home for the class vtable.
|
||||
void raw_ostream::handle() {}
|
||||
|
||||
size_t raw_ostream::preferred_buffer_size() const {
|
||||
// BUFSIZ is intended to be a reasonable default.
|
||||
return BUFSIZ;
|
||||
}
|
||||
|
||||
void raw_ostream::SetBuffered() {
|
||||
// Ask the subclass to determine an appropriate buffer size.
|
||||
if (size_t Size = preferred_buffer_size())
|
||||
SetBufferSize(Size);
|
||||
else
|
||||
// It may return 0, meaning this stream should be unbuffered.
|
||||
SetUnbuffered();
|
||||
}
|
||||
|
||||
void raw_ostream::SetBufferAndMode(char *BufferStart, size_t Size,
|
||||
BufferKind Mode) {
|
||||
assert(((Mode == Unbuffered && !BufferStart && Size == 0) ||
|
||||
(Mode != Unbuffered && BufferStart && Size != 0)) &&
|
||||
"stream must be unbuffered or have at least one byte");
|
||||
// Make sure the current buffer is free of content (we can't flush here; the
|
||||
// child buffer management logic will be in write_impl).
|
||||
assert(GetNumBytesInBuffer() == 0 && "Current buffer is non-empty!");
|
||||
|
||||
if (BufferMode == InternalBuffer)
|
||||
delete [] OutBufStart;
|
||||
OutBufStart = BufferStart;
|
||||
OutBufEnd = OutBufStart+Size;
|
||||
OutBufCur = OutBufStart;
|
||||
BufferMode = Mode;
|
||||
|
||||
assert(OutBufStart <= OutBufEnd && "Invalid size!");
|
||||
}
|
||||
|
||||
raw_ostream &raw_ostream::operator<<(unsigned long N) {
|
||||
// Zero is a special case.
|
||||
if (N == 0)
|
||||
return *this << '0';
|
||||
|
||||
char NumberBuffer[20];
|
||||
char *EndPtr = NumberBuffer+sizeof(NumberBuffer);
|
||||
char *CurPtr = EndPtr;
|
||||
|
||||
while (N) {
|
||||
*--CurPtr = '0' + char(N % 10);
|
||||
N /= 10;
|
||||
}
|
||||
return write(CurPtr, EndPtr-CurPtr);
|
||||
}
|
||||
|
||||
raw_ostream &raw_ostream::operator<<(long N) {
|
||||
if (N < 0) {
|
||||
*this << '-';
|
||||
// Avoid undefined behavior on LONG_MIN with a cast.
|
||||
N = -(unsigned long)N;
|
||||
}
|
||||
|
||||
return this->operator<<(static_cast<unsigned long>(N));
|
||||
}
|
||||
|
||||
raw_ostream &raw_ostream::operator<<(unsigned long long N) {
|
||||
// Output using 32-bit div/mod when possible.
|
||||
if (N == static_cast<unsigned long>(N))
|
||||
return this->operator<<(static_cast<unsigned long>(N));
|
||||
|
||||
char NumberBuffer[20];
|
||||
char *EndPtr = std::end(NumberBuffer);
|
||||
char *CurPtr = EndPtr;
|
||||
|
||||
while (N) {
|
||||
*--CurPtr = '0' + char(N % 10);
|
||||
N /= 10;
|
||||
}
|
||||
return write(CurPtr, EndPtr-CurPtr);
|
||||
}
|
||||
|
||||
raw_ostream &raw_ostream::operator<<(long long N) {
|
||||
if (N < 0) {
|
||||
*this << '-';
|
||||
// Avoid undefined behavior on INT64_MIN with a cast.
|
||||
N = -(unsigned long long)N;
|
||||
}
|
||||
|
||||
return this->operator<<(static_cast<unsigned long long>(N));
|
||||
}
|
||||
|
||||
raw_ostream &raw_ostream::write_hex(unsigned long long N) {
|
||||
// Zero is a special case.
|
||||
if (N == 0)
|
||||
return *this << '0';
|
||||
|
||||
char NumberBuffer[16];
|
||||
char *EndPtr = std::end(NumberBuffer);
|
||||
char *CurPtr = EndPtr;
|
||||
|
||||
while (N) {
|
||||
unsigned char x = static_cast<unsigned char>(N) % 16;
|
||||
*--CurPtr = hexdigit(x, /*LowerCase*/true);
|
||||
N /= 16;
|
||||
}
|
||||
|
||||
return write(CurPtr, EndPtr-CurPtr);
|
||||
}
|
||||
|
||||
raw_ostream &raw_ostream::write_escaped(StringRef Str,
|
||||
bool UseHexEscapes) {
|
||||
for (unsigned char c : Str) {
|
||||
switch (c) {
|
||||
case '\\':
|
||||
*this << '\\' << '\\';
|
||||
break;
|
||||
case '\t':
|
||||
*this << '\\' << 't';
|
||||
break;
|
||||
case '\n':
|
||||
*this << '\\' << 'n';
|
||||
break;
|
||||
case '"':
|
||||
*this << '\\' << '"';
|
||||
break;
|
||||
default:
|
||||
if (std::isprint(c)) {
|
||||
*this << c;
|
||||
break;
|
||||
}
|
||||
|
||||
// Write out the escaped representation.
|
||||
if (UseHexEscapes) {
|
||||
*this << '\\' << 'x';
|
||||
*this << hexdigit((c >> 4 & 0xF));
|
||||
*this << hexdigit((c >> 0) & 0xF);
|
||||
} else {
|
||||
// Always use a full 3-character octal escape.
|
||||
*this << '\\';
|
||||
*this << char('0' + ((c >> 6) & 7));
|
||||
*this << char('0' + ((c >> 3) & 7));
|
||||
*this << char('0' + ((c >> 0) & 7));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
raw_ostream &raw_ostream::operator<<(const void *P) {
|
||||
*this << '0' << 'x';
|
||||
|
||||
return write_hex((uintptr_t) P);
|
||||
}
|
||||
|
||||
raw_ostream &raw_ostream::operator<<(double N) {
|
||||
#ifdef _WIN32
|
||||
// On MSVCRT and compatible, output of %e is incompatible to Posix
|
||||
// by default. Number of exponent digits should be at least 2. "%+03d"
|
||||
// FIXME: Implement our formatter to here or Support/Format.h!
|
||||
#if defined(__MINGW32__)
|
||||
// FIXME: It should be generic to C++11.
|
||||
if (N == 0.0 && std::signbit(N))
|
||||
return *this << "-0.000000e+00";
|
||||
#else
|
||||
int fpcl = _fpclass(N);
|
||||
|
||||
// negative zero
|
||||
if (fpcl == _FPCLASS_NZ)
|
||||
return *this << "-0.000000e+00";
|
||||
#endif
|
||||
|
||||
char buf[16];
|
||||
unsigned len;
|
||||
len = format("%e", N).snprint(buf, sizeof(buf));
|
||||
if (len <= sizeof(buf) - 2) {
|
||||
if (len >= 5 && buf[len - 5] == 'e' && buf[len - 3] == '0') {
|
||||
int cs = buf[len - 4];
|
||||
if (cs == '+' || cs == '-') {
|
||||
int c1 = buf[len - 2];
|
||||
int c0 = buf[len - 1];
|
||||
if (isdigit(static_cast<unsigned char>(c1)) &&
|
||||
isdigit(static_cast<unsigned char>(c0))) {
|
||||
// Trim leading '0': "...e+012" -> "...e+12\0"
|
||||
buf[len - 3] = c1;
|
||||
buf[len - 2] = c0;
|
||||
buf[--len] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return this->operator<<(buf);
|
||||
}
|
||||
#endif
|
||||
return this->operator<<(format("%e", N));
|
||||
}
|
||||
|
||||
|
||||
|
||||
void raw_ostream::flush_nonempty() {
|
||||
assert(OutBufCur > OutBufStart && "Invalid call to flush_nonempty.");
|
||||
size_t Length = OutBufCur - OutBufStart;
|
||||
OutBufCur = OutBufStart;
|
||||
write_impl(OutBufStart, Length);
|
||||
}
|
||||
|
||||
raw_ostream &raw_ostream::write(unsigned char C) {
|
||||
// Group exceptional cases into a single branch.
|
||||
if (LLVM_UNLIKELY(OutBufCur >= OutBufEnd)) {
|
||||
if (LLVM_UNLIKELY(!OutBufStart)) {
|
||||
if (BufferMode == Unbuffered) {
|
||||
write_impl(reinterpret_cast<char*>(&C), 1);
|
||||
return *this;
|
||||
}
|
||||
// Set up a buffer and start over.
|
||||
SetBuffered();
|
||||
return write(C);
|
||||
}
|
||||
|
||||
flush_nonempty();
|
||||
}
|
||||
|
||||
*OutBufCur++ = C;
|
||||
return *this;
|
||||
}
|
||||
|
||||
raw_ostream &raw_ostream::write(const char *Ptr, size_t Size) {
|
||||
// Group exceptional cases into a single branch.
|
||||
if (LLVM_UNLIKELY(size_t(OutBufEnd - OutBufCur) < Size)) {
|
||||
if (LLVM_UNLIKELY(!OutBufStart)) {
|
||||
if (BufferMode == Unbuffered) {
|
||||
write_impl(Ptr, Size);
|
||||
return *this;
|
||||
}
|
||||
// Set up a buffer and start over.
|
||||
SetBuffered();
|
||||
return write(Ptr, Size);
|
||||
}
|
||||
|
||||
size_t NumBytes = OutBufEnd - OutBufCur;
|
||||
|
||||
// If the buffer is empty at this point we have a string that is larger
|
||||
// than the buffer. Directly write the chunk that is a multiple of the
|
||||
// preferred buffer size and put the remainder in the buffer.
|
||||
if (LLVM_UNLIKELY(OutBufCur == OutBufStart)) {
|
||||
assert(NumBytes != 0 && "undefined behavior");
|
||||
size_t BytesToWrite = Size - (Size % NumBytes);
|
||||
write_impl(Ptr, BytesToWrite);
|
||||
size_t BytesRemaining = Size - BytesToWrite;
|
||||
if (BytesRemaining > size_t(OutBufEnd - OutBufCur)) {
|
||||
// Too much left over to copy into our buffer.
|
||||
return write(Ptr + BytesToWrite, BytesRemaining);
|
||||
}
|
||||
copy_to_buffer(Ptr + BytesToWrite, BytesRemaining);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// We don't have enough space in the buffer to fit the string in. Insert as
|
||||
// much as possible, flush and start over with the remainder.
|
||||
copy_to_buffer(Ptr, NumBytes);
|
||||
flush_nonempty();
|
||||
return write(Ptr + NumBytes, Size - NumBytes);
|
||||
}
|
||||
|
||||
copy_to_buffer(Ptr, Size);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void raw_ostream::copy_to_buffer(const char *Ptr, size_t Size) {
|
||||
assert(Size <= size_t(OutBufEnd - OutBufCur) && "Buffer overrun!");
|
||||
|
||||
// Handle short strings specially, memcpy isn't very good at very short
|
||||
// strings.
|
||||
switch (Size) {
|
||||
case 4: OutBufCur[3] = Ptr[3]; // FALL THROUGH
|
||||
case 3: OutBufCur[2] = Ptr[2]; // FALL THROUGH
|
||||
case 2: OutBufCur[1] = Ptr[1]; // FALL THROUGH
|
||||
case 1: OutBufCur[0] = Ptr[0]; // FALL THROUGH
|
||||
case 0: break;
|
||||
default:
|
||||
memcpy(OutBufCur, Ptr, Size);
|
||||
break;
|
||||
}
|
||||
|
||||
OutBufCur += Size;
|
||||
}
|
||||
|
||||
// Formatted output.
|
||||
raw_ostream &raw_ostream::operator<<(const format_object_base &Fmt) {
|
||||
// If we have more than a few bytes left in our output buffer, try
|
||||
// formatting directly onto its end.
|
||||
size_t NextBufferSize = 127;
|
||||
size_t BufferBytesLeft = OutBufEnd - OutBufCur;
|
||||
if (BufferBytesLeft > 3) {
|
||||
size_t BytesUsed = Fmt.print(OutBufCur, BufferBytesLeft);
|
||||
|
||||
// Common case is that we have plenty of space.
|
||||
if (BytesUsed <= BufferBytesLeft) {
|
||||
OutBufCur += BytesUsed;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Otherwise, we overflowed and the return value tells us the size to try
|
||||
// again with.
|
||||
NextBufferSize = BytesUsed;
|
||||
}
|
||||
|
||||
// If we got here, we didn't have enough space in the output buffer for the
|
||||
// string. Try printing into a SmallVector that is resized to have enough
|
||||
// space. Iterate until we win.
|
||||
SmallVector<char, 128> V;
|
||||
|
||||
while (1) {
|
||||
V.resize(NextBufferSize);
|
||||
|
||||
// Try formatting into the SmallVector.
|
||||
size_t BytesUsed = Fmt.print(V.data(), NextBufferSize);
|
||||
|
||||
// If BytesUsed fit into the vector, we win.
|
||||
if (BytesUsed <= NextBufferSize)
|
||||
return write(V.data(), BytesUsed);
|
||||
|
||||
// Otherwise, try again with a new size.
|
||||
assert(BytesUsed > NextBufferSize && "Didn't grow buffer!?");
|
||||
NextBufferSize = BytesUsed;
|
||||
}
|
||||
}
|
||||
|
||||
raw_ostream &raw_ostream::operator<<(const FormattedString &FS) {
|
||||
unsigned Len = FS.Str.size();
|
||||
int PadAmount = FS.Width - Len;
|
||||
if (FS.RightJustify && (PadAmount > 0))
|
||||
this->indent(PadAmount);
|
||||
this->operator<<(FS.Str);
|
||||
if (!FS.RightJustify && (PadAmount > 0))
|
||||
this->indent(PadAmount);
|
||||
return *this;
|
||||
}
|
||||
|
||||
raw_ostream &raw_ostream::operator<<(const FormattedNumber &FN) {
|
||||
if (FN.Hex) {
|
||||
unsigned Nibbles = (64 - countLeadingZeros(FN.HexValue)+3)/4;
|
||||
unsigned PrefixChars = FN.HexPrefix ? 2 : 0;
|
||||
unsigned Width = std::max(FN.Width, Nibbles + PrefixChars);
|
||||
|
||||
char NumberBuffer[20] = "0x0000000000000000";
|
||||
if (!FN.HexPrefix)
|
||||
NumberBuffer[1] = '0';
|
||||
char *EndPtr = NumberBuffer+Width;
|
||||
char *CurPtr = EndPtr;
|
||||
unsigned long long N = FN.HexValue;
|
||||
while (N) {
|
||||
unsigned char x = static_cast<unsigned char>(N) % 16;
|
||||
*--CurPtr = hexdigit(x, !FN.Upper);
|
||||
N /= 16;
|
||||
}
|
||||
|
||||
return write(NumberBuffer, Width);
|
||||
} else {
|
||||
// Zero is a special case.
|
||||
if (FN.DecValue == 0) {
|
||||
this->indent(FN.Width-1);
|
||||
return *this << '0';
|
||||
}
|
||||
char NumberBuffer[32];
|
||||
char *EndPtr = NumberBuffer+sizeof(NumberBuffer);
|
||||
char *CurPtr = EndPtr;
|
||||
bool Neg = (FN.DecValue < 0);
|
||||
uint64_t N = Neg ? -static_cast<uint64_t>(FN.DecValue) : FN.DecValue;
|
||||
while (N) {
|
||||
*--CurPtr = '0' + char(N % 10);
|
||||
N /= 10;
|
||||
}
|
||||
int Len = EndPtr - CurPtr;
|
||||
int Pad = FN.Width - Len;
|
||||
if (Neg)
|
||||
--Pad;
|
||||
if (Pad > 0)
|
||||
this->indent(Pad);
|
||||
if (Neg)
|
||||
*this << '-';
|
||||
return write(CurPtr, Len);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// indent - Insert 'NumSpaces' spaces.
|
||||
raw_ostream &raw_ostream::indent(unsigned NumSpaces) {
|
||||
static const char Spaces[] = " "
|
||||
" "
|
||||
" ";
|
||||
|
||||
// Usually the indentation is small, handle it with a fastpath.
|
||||
if (NumSpaces < array_lengthof(Spaces))
|
||||
return write(Spaces, NumSpaces);
|
||||
|
||||
while (NumSpaces) {
|
||||
unsigned NumToWrite = std::min(NumSpaces,
|
||||
(unsigned)array_lengthof(Spaces)-1);
|
||||
write(Spaces, NumToWrite);
|
||||
NumSpaces -= NumToWrite;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Formatted Output
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// Out of line virtual method.
|
||||
void format_object_base::home() {
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// raw_fd_ostream
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
static int getFD(StringRef Filename, std::error_code &EC,
|
||||
sys::fs::OpenFlags Flags) {
|
||||
// Handle "-" as stdout. Note that when we do this, we consider ourself
|
||||
// the owner of stdout. This means that we can do things like close the
|
||||
// file descriptor when we're done and set the "binary" flag globally.
|
||||
if (Filename == "-") {
|
||||
EC = std::error_code();
|
||||
// If user requested binary then put stdout into binary mode if
|
||||
// possible.
|
||||
if (!(Flags & sys::fs::F_Text)) {
|
||||
#if defined(_WIN32)
|
||||
_setmode(_fileno(stdout), _O_BINARY);
|
||||
#endif
|
||||
}
|
||||
return STDOUT_FILENO;
|
||||
}
|
||||
|
||||
int FD;
|
||||
|
||||
EC = sys::fs::openFileForWrite(Filename, FD, Flags);
|
||||
if (EC)
|
||||
return -1;
|
||||
|
||||
EC = std::error_code();
|
||||
return FD;
|
||||
}
|
||||
|
||||
raw_fd_ostream::raw_fd_ostream(StringRef Filename, std::error_code &EC,
|
||||
sys::fs::OpenFlags Flags)
|
||||
: raw_fd_ostream(getFD(Filename, EC, Flags), true) {}
|
||||
|
||||
/// FD is the file descriptor that this writes to. If ShouldClose is true, this
|
||||
/// closes the file when the stream is destroyed.
|
||||
raw_fd_ostream::raw_fd_ostream(int fd, bool shouldClose, bool unbuffered)
|
||||
: raw_pwrite_stream(unbuffered), FD(fd), ShouldClose(shouldClose),
|
||||
Error(false) {
|
||||
if (FD < 0 ) {
|
||||
ShouldClose = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the starting position.
|
||||
off_t loc = ::lseek(FD, 0, SEEK_CUR);
|
||||
#ifdef _WIN32
|
||||
// MSVCRT's _lseek(SEEK_CUR) doesn't return -1 for pipes.
|
||||
SupportsSeeking = loc != (off_t)-1 && ::GetFileType(reinterpret_cast<HANDLE>(::_get_osfhandle(FD))) != FILE_TYPE_PIPE;
|
||||
#else
|
||||
SupportsSeeking = loc != (off_t)-1;
|
||||
#endif
|
||||
if (!SupportsSeeking)
|
||||
pos = 0;
|
||||
else
|
||||
pos = static_cast<uint64_t>(loc);
|
||||
}
|
||||
|
||||
raw_fd_ostream::~raw_fd_ostream() {
|
||||
if (FD >= 0) {
|
||||
flush();
|
||||
if (ShouldClose && ::close(FD) < 0)
|
||||
error_detected();
|
||||
}
|
||||
|
||||
#ifdef __MINGW32__
|
||||
// On mingw, global dtors should not call exit().
|
||||
// report_fatal_error() invokes exit(). We know report_fatal_error()
|
||||
// might not write messages to stderr when any errors were detected
|
||||
// on FD == 2.
|
||||
if (FD == 2) return;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void raw_fd_ostream::write_impl(const char *Ptr, size_t Size) {
|
||||
assert(FD >= 0 && "File already closed.");
|
||||
pos += Size;
|
||||
|
||||
#ifndef _WIN32
|
||||
bool ShouldWriteInChunks = false;
|
||||
#else
|
||||
// Writing a large size of output to Windows console returns ENOMEM. It seems
|
||||
// that, prior to Windows 8, WriteFile() is redirecting to WriteConsole(), and
|
||||
// the latter has a size limit (66000 bytes or less, depending on heap usage).
|
||||
bool ShouldWriteInChunks = !!::_isatty(FD) && !RunningWindows8OrGreater();
|
||||
#endif
|
||||
|
||||
do {
|
||||
size_t ChunkSize = Size;
|
||||
if (ChunkSize > 32767 && ShouldWriteInChunks)
|
||||
ChunkSize = 32767;
|
||||
|
||||
#ifdef _WIN32
|
||||
int ret = ::_write(FD, Ptr, ChunkSize);
|
||||
#else
|
||||
ssize_t ret = ::write(FD, Ptr, ChunkSize);
|
||||
#endif
|
||||
|
||||
if (ret < 0) {
|
||||
// If it's a recoverable error, swallow it and retry the write.
|
||||
//
|
||||
// Ideally we wouldn't ever see EAGAIN or EWOULDBLOCK here, since
|
||||
// raw_ostream isn't designed to do non-blocking I/O. However, some
|
||||
// programs, such as old versions of bjam, have mistakenly used
|
||||
// O_NONBLOCK. For compatibility, emulate blocking semantics by
|
||||
// spinning until the write succeeds. If you don't want spinning,
|
||||
// don't use O_NONBLOCK file descriptors with raw_ostream.
|
||||
if (errno == EINTR || errno == EAGAIN
|
||||
#ifdef EWOULDBLOCK
|
||||
|| errno == EWOULDBLOCK
|
||||
#endif
|
||||
)
|
||||
continue;
|
||||
|
||||
// Otherwise it's a non-recoverable error. Note it and quit.
|
||||
error_detected();
|
||||
break;
|
||||
}
|
||||
|
||||
// The write may have written some or all of the data. Update the
|
||||
// size and buffer pointer to reflect the remainder that needs
|
||||
// to be written. If there are no bytes left, we're done.
|
||||
Ptr += ret;
|
||||
Size -= ret;
|
||||
} while (Size > 0);
|
||||
}
|
||||
|
||||
void raw_fd_ostream::close() {
|
||||
assert(ShouldClose);
|
||||
ShouldClose = false;
|
||||
flush();
|
||||
if (::close(FD) < 0)
|
||||
error_detected();
|
||||
FD = -1;
|
||||
}
|
||||
|
||||
uint64_t raw_fd_ostream::seek(uint64_t off) {
|
||||
assert(SupportsSeeking && "Stream does not support seeking!");
|
||||
flush();
|
||||
pos = ::lseek(FD, off, SEEK_SET);
|
||||
if (pos == (uint64_t)-1)
|
||||
error_detected();
|
||||
return pos;
|
||||
}
|
||||
|
||||
void raw_fd_ostream::pwrite_impl(const char *Ptr, size_t Size,
|
||||
uint64_t Offset) {
|
||||
uint64_t Pos = tell();
|
||||
seek(Offset);
|
||||
write(Ptr, Size);
|
||||
seek(Pos);
|
||||
}
|
||||
|
||||
size_t raw_fd_ostream::preferred_buffer_size() const {
|
||||
#if !defined(_MSC_VER) && !defined(__MINGW32__) && !defined(__minix)
|
||||
// Windows and Minix have no st_blksize.
|
||||
assert(FD >= 0 && "File not yet open!");
|
||||
struct stat statbuf;
|
||||
if (fstat(FD, &statbuf) != 0)
|
||||
return 0;
|
||||
|
||||
// If this is a terminal, don't use buffering. Line buffering
|
||||
// would be a more traditional thing to do, but it's not worth
|
||||
// the complexity.
|
||||
if (S_ISCHR(statbuf.st_mode) && isatty(FD))
|
||||
return 0;
|
||||
// Return the preferred block size.
|
||||
return statbuf.st_blksize;
|
||||
#else
|
||||
return raw_ostream::preferred_buffer_size();
|
||||
#endif
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// outs(), errs(), nulls()
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// outs() - This returns a reference to a raw_ostream for standard output.
|
||||
/// Use it like: outs() << "foo" << "bar";
|
||||
raw_ostream &llvm::outs() {
|
||||
// Set buffer settings to model stdout behavior. Delete the file descriptor
|
||||
// when the program exits, forcing error detection. This means that if you
|
||||
// ever call outs(), you can't open another raw_fd_ostream on stdout, as we'll
|
||||
// close stdout twice and print an error the second time.
|
||||
std::error_code EC;
|
||||
static raw_fd_ostream S("-", EC, sys::fs::F_None);
|
||||
assert(!EC);
|
||||
return S;
|
||||
}
|
||||
|
||||
/// errs() - This returns a reference to a raw_ostream for standard error.
|
||||
/// Use it like: errs() << "foo" << "bar";
|
||||
raw_ostream &llvm::errs() {
|
||||
// Set standard error to be unbuffered by default.
|
||||
static raw_fd_ostream S(STDERR_FILENO, false, true);
|
||||
return S;
|
||||
}
|
||||
|
||||
/// nulls() - This returns a reference to a raw_ostream which discards output.
|
||||
raw_ostream &llvm::nulls() {
|
||||
static raw_null_ostream S;
|
||||
return S;
|
||||
}
|
||||
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// raw_string_ostream
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
raw_string_ostream::~raw_string_ostream() {
|
||||
flush();
|
||||
}
|
||||
|
||||
void raw_string_ostream::write_impl(const char *Ptr, size_t Size) {
|
||||
OS.append(Ptr, Size);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// raw_svector_ostream
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
uint64_t raw_svector_ostream::current_pos() const { return OS.size(); }
|
||||
|
||||
void raw_svector_ostream::write_impl(const char *Ptr, size_t Size) {
|
||||
OS.append(Ptr, Ptr + Size);
|
||||
}
|
||||
|
||||
void raw_svector_ostream::pwrite_impl(const char *Ptr, size_t Size,
|
||||
uint64_t Offset) {
|
||||
memcpy(OS.data() + Offset, Ptr, Size);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// raw_null_ostream
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
raw_null_ostream::~raw_null_ostream() {
|
||||
#ifndef NDEBUG
|
||||
// ~raw_ostream asserts that the buffer is empty. This isn't necessary
|
||||
// with raw_null_ostream, but it's better to have raw_null_ostream follow
|
||||
// the rules than to change the rules just for raw_null_ostream.
|
||||
flush();
|
||||
#endif
|
||||
}
|
||||
|
||||
void raw_null_ostream::write_impl(const char * /*Ptr*/, size_t /*Size*/) {}
|
||||
|
||||
uint64_t raw_null_ostream::current_pos() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void raw_null_ostream::pwrite_impl(const char * /*Ptr*/, size_t /*Size*/,
|
||||
uint64_t /*Offset*/) {}
|
||||
171
wpiutil/src/main/native/cpp/support/Base64.cpp
Normal file
171
wpiutil/src/main/native/cpp/support/Base64.cpp
Normal file
@@ -0,0 +1,171 @@
|
||||
/* ====================================================================
|
||||
* Copyright (c) 1995-1999 The Apache Group. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. All advertising materials mentioning features or use of this
|
||||
* software must display the following acknowledgment:
|
||||
* "This product includes software developed by the Apache Group
|
||||
* for use in the Apache HTTP server project (http://www.apache.org/)."
|
||||
*
|
||||
* 4. The names "Apache Server" and "Apache Group" must not be used to
|
||||
* endorse or promote products derived from this software without
|
||||
* prior written permission. For written permission, please contact
|
||||
* apache@apache.org.
|
||||
*
|
||||
* 5. Products derived from this software may not be called "Apache"
|
||||
* nor may "Apache" appear in their names without prior written
|
||||
* permission of the Apache Group.
|
||||
*
|
||||
* 6. Redistributions of any form whatsoever must retain the following
|
||||
* acknowledgment:
|
||||
* "This product includes software developed by the Apache Group
|
||||
* for use in the Apache HTTP server project (http://www.apache.org/)."
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
|
||||
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
|
||||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Group and was originally based
|
||||
* on public domain software written at the National Center for
|
||||
* Supercomputing Applications, University of Illinois, Urbana-Champaign.
|
||||
* For more information on the Apache Group and the Apache HTTP server
|
||||
* project, please see <http://www.apache.org/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "support/Base64.h"
|
||||
|
||||
#include "llvm/SmallVector.h"
|
||||
#include "llvm/raw_ostream.h"
|
||||
|
||||
namespace wpi {
|
||||
|
||||
// aaaack but it's fast and const should make it shared text page.
|
||||
static const unsigned char pr2six[256] = {
|
||||
// ASCII table
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 62, 64, 64, 64, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60,
|
||||
61, 64, 64, 64, 64, 64, 64, 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
|
||||
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64,
|
||||
64, 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
|
||||
43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||
64, 64, 64, 64, 64, 64, 64, 64, 64};
|
||||
|
||||
size_t Base64Decode(llvm::raw_ostream& os, llvm::StringRef encoded) {
|
||||
const unsigned char* end = encoded.bytes_begin();
|
||||
while (pr2six[*end] <= 63 && end != encoded.bytes_end()) ++end;
|
||||
size_t nprbytes = end - encoded.bytes_begin();
|
||||
if (nprbytes == 0) return 0;
|
||||
|
||||
const unsigned char* cur = encoded.bytes_begin();
|
||||
|
||||
while (nprbytes > 4) {
|
||||
os << static_cast<unsigned char>(pr2six[cur[0]] << 2 | pr2six[cur[1]] >> 4);
|
||||
os << static_cast<unsigned char>(pr2six[cur[1]] << 4 | pr2six[cur[2]] >> 2);
|
||||
os << static_cast<unsigned char>(pr2six[cur[2]] << 6 | pr2six[cur[3]]);
|
||||
cur += 4;
|
||||
nprbytes -= 4;
|
||||
}
|
||||
|
||||
// Note: (nprbytes == 1) would be an error, so just ignore that case
|
||||
if (nprbytes > 1)
|
||||
os << static_cast<unsigned char>(pr2six[cur[0]] << 2 | pr2six[cur[1]] >> 4);
|
||||
if (nprbytes > 2)
|
||||
os << static_cast<unsigned char>(pr2six[cur[1]] << 4 | pr2six[cur[2]] >> 2);
|
||||
if (nprbytes > 3)
|
||||
os << static_cast<unsigned char>(pr2six[cur[2]] << 6 | pr2six[cur[3]]);
|
||||
|
||||
return (end - encoded.bytes_begin()) + ((4 - nprbytes) & 3);
|
||||
}
|
||||
|
||||
size_t Base64Decode(llvm::StringRef encoded, std::string* plain) {
|
||||
plain->resize(0);
|
||||
llvm::raw_string_ostream os(*plain);
|
||||
size_t rv = Base64Decode(os, encoded);
|
||||
os.flush();
|
||||
return rv;
|
||||
}
|
||||
|
||||
llvm::StringRef Base64Decode(llvm::StringRef encoded, size_t* num_read,
|
||||
llvm::SmallVectorImpl<char>& buf) {
|
||||
buf.clear();
|
||||
llvm::raw_svector_ostream os(buf);
|
||||
*num_read = Base64Decode(os, encoded);
|
||||
return os.str();
|
||||
}
|
||||
|
||||
static const char basis_64[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
void Base64Encode(llvm::raw_ostream& os, llvm::StringRef plain) {
|
||||
if (plain.empty()) return;
|
||||
size_t len = plain.size();
|
||||
|
||||
size_t i;
|
||||
for (i = 0; (i + 2) < len; i += 3) {
|
||||
os << basis_64[(plain[i] >> 2) & 0x3F];
|
||||
os << basis_64[((plain[i] & 0x3) << 4) |
|
||||
(static_cast<int>(plain[i + 1] & 0xF0) >> 4)];
|
||||
os << basis_64[((plain[i + 1] & 0xF) << 2) |
|
||||
(static_cast<int>(plain[i + 2] & 0xC0) >> 6)];
|
||||
os << basis_64[plain[i + 2] & 0x3F];
|
||||
}
|
||||
if (i < len) {
|
||||
os << basis_64[(plain[i] >> 2) & 0x3F];
|
||||
if (i == (len - 1)) {
|
||||
os << basis_64[((plain[i] & 0x3) << 4)];
|
||||
os << '=';
|
||||
} else {
|
||||
os << basis_64[((plain[i] & 0x3) << 4) |
|
||||
(static_cast<int>(plain[i + 1] & 0xF0) >> 4)];
|
||||
os << basis_64[((plain[i + 1] & 0xF) << 2)];
|
||||
}
|
||||
os << '=';
|
||||
}
|
||||
}
|
||||
|
||||
void Base64Encode(llvm::StringRef plain, std::string* encoded) {
|
||||
encoded->resize(0);
|
||||
llvm::raw_string_ostream os(*encoded);
|
||||
Base64Encode(os, plain);
|
||||
os.flush();
|
||||
}
|
||||
|
||||
llvm::StringRef Base64Encode(llvm::StringRef plain,
|
||||
llvm::SmallVectorImpl<char>& buf) {
|
||||
buf.clear();
|
||||
llvm::raw_svector_ostream os(buf);
|
||||
Base64Encode(os, plain);
|
||||
return os.str();
|
||||
}
|
||||
|
||||
} // namespace wpi
|
||||
336
wpiutil/src/main/native/cpp/support/HttpUtil.cpp
Normal file
336
wpiutil/src/main/native/cpp/support/HttpUtil.cpp
Normal file
@@ -0,0 +1,336 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2016-2018 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "support/HttpUtil.h"
|
||||
|
||||
#include <cctype>
|
||||
|
||||
#include "llvm/STLExtras.h"
|
||||
#include "llvm/StringExtras.h"
|
||||
#include "llvm/raw_ostream.h"
|
||||
#include "support/Base64.h"
|
||||
#include "tcpsockets/TCPConnector.h"
|
||||
|
||||
namespace wpi {
|
||||
|
||||
llvm::StringRef UnescapeURI(const llvm::Twine& str,
|
||||
llvm::SmallVectorImpl<char>& buf, bool* error) {
|
||||
llvm::SmallString<128> strBuf;
|
||||
llvm::StringRef strStr = str.toStringRef(strBuf);
|
||||
buf.clear();
|
||||
for (auto i = strStr.begin(), end = strStr.end(); i != end; ++i) {
|
||||
// pass non-escaped characters to output
|
||||
if (*i != '%') {
|
||||
// decode + to space
|
||||
if (*i == '+')
|
||||
buf.push_back(' ');
|
||||
else
|
||||
buf.push_back(*i);
|
||||
continue;
|
||||
}
|
||||
|
||||
// are there enough characters left?
|
||||
if (i + 2 >= end) {
|
||||
*error = true;
|
||||
return llvm::StringRef{};
|
||||
}
|
||||
|
||||
// replace %xx with the corresponding character
|
||||
unsigned val1 = llvm::hexDigitValue(*++i);
|
||||
if (val1 == -1U) {
|
||||
*error = true;
|
||||
return llvm::StringRef{};
|
||||
}
|
||||
unsigned val2 = llvm::hexDigitValue(*++i);
|
||||
if (val2 == -1U) {
|
||||
*error = true;
|
||||
return llvm::StringRef{};
|
||||
}
|
||||
buf.push_back((val1 << 4) | val2);
|
||||
}
|
||||
|
||||
*error = false;
|
||||
return llvm::StringRef{buf.data(), buf.size()};
|
||||
}
|
||||
|
||||
llvm::StringRef EscapeURI(const llvm::Twine& str,
|
||||
llvm::SmallVectorImpl<char>& buf, bool spacePlus) {
|
||||
static const char* const hexLut = "0123456789ABCDEF";
|
||||
|
||||
llvm::SmallString<128> strBuf;
|
||||
llvm::StringRef strStr = str.toStringRef(strBuf);
|
||||
buf.clear();
|
||||
for (auto i = strStr.begin(), end = strStr.end(); i != end; ++i) {
|
||||
// pass unreserved characters to output
|
||||
if (std::isalnum(*i) || *i == '-' || *i == '_' || *i == '.' || *i == '~') {
|
||||
buf.push_back(*i);
|
||||
continue;
|
||||
}
|
||||
|
||||
// encode space to +
|
||||
if (spacePlus && *i == ' ') {
|
||||
buf.push_back('+');
|
||||
continue;
|
||||
}
|
||||
|
||||
// convert others to %xx
|
||||
buf.push_back('%');
|
||||
buf.push_back(hexLut[((*i) >> 4) & 0x0f]);
|
||||
buf.push_back(hexLut[(*i) & 0x0f]);
|
||||
}
|
||||
|
||||
return llvm::StringRef{buf.data(), buf.size()};
|
||||
}
|
||||
|
||||
bool ParseHttpHeaders(raw_istream& is, llvm::SmallVectorImpl<char>* contentType,
|
||||
llvm::SmallVectorImpl<char>* contentLength) {
|
||||
if (contentType) contentType->clear();
|
||||
if (contentLength) contentLength->clear();
|
||||
|
||||
bool inContentType = false;
|
||||
bool inContentLength = false;
|
||||
llvm::SmallString<64> lineBuf;
|
||||
for (;;) {
|
||||
llvm::StringRef line = is.getline(lineBuf, 1024).rtrim();
|
||||
if (is.has_error()) return false;
|
||||
if (line.empty()) return true; // empty line signals end of headers
|
||||
|
||||
// header fields start at the beginning of the line
|
||||
if (!std::isspace(line[0])) {
|
||||
inContentType = false;
|
||||
inContentLength = false;
|
||||
llvm::StringRef field;
|
||||
std::tie(field, line) = line.split(':');
|
||||
field = field.rtrim();
|
||||
if (field.equals_lower("content-type"))
|
||||
inContentType = true;
|
||||
else if (field.equals_lower("content-length"))
|
||||
inContentLength = true;
|
||||
else
|
||||
continue; // ignore other fields
|
||||
}
|
||||
|
||||
// collapse whitespace
|
||||
line = line.ltrim();
|
||||
|
||||
// save field data
|
||||
if (inContentType && contentType)
|
||||
contentType->append(line.begin(), line.end());
|
||||
else if (inContentLength && contentLength)
|
||||
contentLength->append(line.begin(), line.end());
|
||||
}
|
||||
}
|
||||
|
||||
bool FindMultipartBoundary(raw_istream& is, llvm::StringRef boundary,
|
||||
std::string* saveBuf) {
|
||||
llvm::SmallString<64> searchBuf;
|
||||
searchBuf.resize(boundary.size() + 2);
|
||||
size_t searchPos = 0;
|
||||
|
||||
// Per the spec, the --boundary should be preceded by \r\n, so do a first
|
||||
// pass of 1-byte reads to throw those away (common case) and keep the
|
||||
// last non-\r\n character in searchBuf.
|
||||
if (!saveBuf) {
|
||||
do {
|
||||
is.read(searchBuf.data(), 1);
|
||||
if (is.has_error()) return false;
|
||||
} while (searchBuf[0] == '\r' || searchBuf[0] == '\n');
|
||||
searchPos = 1;
|
||||
}
|
||||
|
||||
// Look for --boundary. Read boundarysize+2 bytes at a time
|
||||
// during the search to speed up the reads, then fast-scan for -,
|
||||
// and only then match the entire boundary. This will be slow if
|
||||
// there's a bunch of continuous -'s in the output, but that's unlikely.
|
||||
for (;;) {
|
||||
is.read(searchBuf.data() + searchPos, searchBuf.size() - searchPos);
|
||||
if (is.has_error()) return false;
|
||||
|
||||
// Did we find the boundary?
|
||||
if (searchBuf[0] == '-' && searchBuf[1] == '-' &&
|
||||
searchBuf.substr(2) == boundary)
|
||||
return true;
|
||||
|
||||
// Fast-scan for '-'
|
||||
size_t pos = searchBuf.find('-', searchBuf[0] == '-' ? 1 : 0);
|
||||
if (pos == llvm::StringRef::npos) {
|
||||
if (saveBuf) saveBuf->append(searchBuf.data(), searchBuf.size());
|
||||
} else {
|
||||
if (saveBuf) saveBuf->append(searchBuf.data(), pos);
|
||||
|
||||
// move '-' and following to start of buffer (next read will fill)
|
||||
std::memmove(searchBuf.data(), searchBuf.data() + pos,
|
||||
searchBuf.size() - pos);
|
||||
searchPos = searchBuf.size() - pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HttpLocation::HttpLocation(const llvm::Twine& url_, bool* error,
|
||||
std::string* errorMsg)
|
||||
: url{url_.str()} {
|
||||
// Split apart into components
|
||||
llvm::StringRef query{url};
|
||||
|
||||
// scheme:
|
||||
llvm::StringRef scheme;
|
||||
std::tie(scheme, query) = query.split(':');
|
||||
if (!scheme.equals_lower("http")) {
|
||||
*errorMsg = "only supports http URLs";
|
||||
*error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// "//"
|
||||
if (!query.startswith("//")) {
|
||||
*errorMsg = "expected http://...";
|
||||
*error = true;
|
||||
return;
|
||||
}
|
||||
query = query.drop_front(2);
|
||||
|
||||
// user:password@host:port/
|
||||
llvm::StringRef authority;
|
||||
std::tie(authority, query) = query.split('/');
|
||||
|
||||
llvm::StringRef userpass, hostport;
|
||||
std::tie(userpass, hostport) = authority.split('@');
|
||||
// split leaves the RHS empty if the split char isn't present...
|
||||
if (hostport.empty()) {
|
||||
hostport = userpass;
|
||||
userpass = llvm::StringRef{};
|
||||
}
|
||||
|
||||
if (!userpass.empty()) {
|
||||
llvm::StringRef rawUser, rawPassword;
|
||||
std::tie(rawUser, rawPassword) = userpass.split(':');
|
||||
llvm::SmallString<64> userBuf, passBuf;
|
||||
user = UnescapeURI(rawUser, userBuf, error);
|
||||
if (*error) {
|
||||
llvm::raw_string_ostream oss(*errorMsg);
|
||||
oss << "could not unescape user \"" << rawUser << "\"";
|
||||
oss.flush();
|
||||
return;
|
||||
}
|
||||
password = UnescapeURI(rawPassword, passBuf, error);
|
||||
if (*error) {
|
||||
llvm::raw_string_ostream oss(*errorMsg);
|
||||
oss << "could not unescape password \"" << rawPassword << "\"";
|
||||
oss.flush();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
llvm::StringRef portStr;
|
||||
std::tie(host, portStr) = hostport.rsplit(':');
|
||||
if (host.empty()) {
|
||||
*errorMsg = "host is empty";
|
||||
*error = true;
|
||||
return;
|
||||
}
|
||||
if (portStr.empty()) {
|
||||
port = 80;
|
||||
} else if (portStr.getAsInteger(10, port)) {
|
||||
llvm::raw_string_ostream oss(*errorMsg);
|
||||
oss << "port \"" << portStr << "\" is not an integer";
|
||||
oss.flush();
|
||||
*error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// path?query#fragment
|
||||
std::tie(query, fragment) = query.split('#');
|
||||
std::tie(path, query) = query.split('?');
|
||||
|
||||
// Split query string into parameters
|
||||
while (!query.empty()) {
|
||||
// split out next param and value
|
||||
llvm::StringRef rawParam, rawValue;
|
||||
std::tie(rawParam, query) = query.split('&');
|
||||
if (rawParam.empty()) continue; // ignore "&&"
|
||||
std::tie(rawParam, rawValue) = rawParam.split('=');
|
||||
|
||||
// unescape param
|
||||
*error = false;
|
||||
llvm::SmallString<64> paramBuf;
|
||||
llvm::StringRef param = UnescapeURI(rawParam, paramBuf, error);
|
||||
if (*error) {
|
||||
llvm::raw_string_ostream oss(*errorMsg);
|
||||
oss << "could not unescape parameter \"" << rawParam << "\"";
|
||||
oss.flush();
|
||||
return;
|
||||
}
|
||||
|
||||
// unescape value
|
||||
llvm::SmallString<64> valueBuf;
|
||||
llvm::StringRef value = UnescapeURI(rawValue, valueBuf, error);
|
||||
if (*error) {
|
||||
llvm::raw_string_ostream oss(*errorMsg);
|
||||
oss << "could not unescape value \"" << rawValue << "\"";
|
||||
oss.flush();
|
||||
return;
|
||||
}
|
||||
|
||||
params.emplace_back(std::make_pair(param, value));
|
||||
}
|
||||
|
||||
*error = false;
|
||||
}
|
||||
|
||||
void HttpRequest::SetAuth(const HttpLocation& loc) {
|
||||
if (!loc.user.empty()) {
|
||||
llvm::SmallString<64> userpass;
|
||||
userpass += loc.user;
|
||||
userpass += ':';
|
||||
userpass += loc.password;
|
||||
Base64Encode(userpass, &auth);
|
||||
}
|
||||
}
|
||||
|
||||
bool HttpConnection::Handshake(const HttpRequest& request,
|
||||
std::string* warnMsg) {
|
||||
// send GET request
|
||||
os << "GET /" << request.path << " HTTP/1.1\r\n";
|
||||
os << "Host: " << request.host << "\r\n";
|
||||
if (!request.auth.empty())
|
||||
os << "Authorization: Basic " << request.auth << "\r\n";
|
||||
os << "\r\n";
|
||||
os.flush();
|
||||
|
||||
// read first line of response
|
||||
llvm::SmallString<64> lineBuf;
|
||||
llvm::StringRef line = is.getline(lineBuf, 1024).rtrim();
|
||||
if (is.has_error()) {
|
||||
*warnMsg = "disconnected before response";
|
||||
return false;
|
||||
}
|
||||
|
||||
// see if we got a HTTP 200 response
|
||||
llvm::StringRef httpver, code, codeText;
|
||||
std::tie(httpver, line) = line.split(' ');
|
||||
std::tie(code, codeText) = line.split(' ');
|
||||
if (!httpver.startswith("HTTP")) {
|
||||
*warnMsg = "did not receive HTTP response";
|
||||
return false;
|
||||
}
|
||||
if (code != "200") {
|
||||
llvm::raw_string_ostream oss(*warnMsg);
|
||||
oss << "received " << code << " " << codeText << " response";
|
||||
oss.flush();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse headers
|
||||
if (!ParseHttpHeaders(is, &contentType, &contentLength)) {
|
||||
*warnMsg = "disconnected during headers";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace wpi
|
||||
65
wpiutil/src/main/native/cpp/support/hostname.cpp
Normal file
65
wpiutil/src/main/native/cpp/support/hostname.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2017-2018 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "support/hostname.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Winsock2.h>
|
||||
#pragma comment(lib, "Ws2_32.lib")
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "llvm/SmallVector.h"
|
||||
#include "llvm/StringRef.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
struct WSAHelper {
|
||||
WSAHelper() {
|
||||
WSAData wsaData;
|
||||
WORD wVersionRequested = MAKEWORD(2, 2);
|
||||
WSAStartup(wVersionRequested, &wsaData);
|
||||
}
|
||||
~WSAHelper() { WSACleanup(); }
|
||||
};
|
||||
static WSAHelper& GetWSAHelper() {
|
||||
static WSAHelper helper;
|
||||
return helper;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace wpi {
|
||||
static bool GetHostnameImpl(char* name, size_t name_len) {
|
||||
#ifdef _WIN32
|
||||
GetWSAHelper();
|
||||
#endif
|
||||
if (::gethostname(name, name_len) != 0) return false;
|
||||
name[name_len - 1] =
|
||||
'\0'; // Per POSIX, may not be null terminated if too long
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string GetHostname() {
|
||||
char name[256];
|
||||
if (!GetHostnameImpl(name, sizeof(name))) return "";
|
||||
return name;
|
||||
}
|
||||
|
||||
llvm::StringRef GetHostname(llvm::SmallVectorImpl<char>& name) {
|
||||
// Use a tmp array to not require the SmallVector to be too large.
|
||||
char tmpName[256];
|
||||
if (!GetHostnameImpl(tmpName, sizeof(tmpName))) {
|
||||
return llvm::StringRef{};
|
||||
}
|
||||
name.clear();
|
||||
name.append(tmpName, tmpName + std::strlen(tmpName) + 1);
|
||||
|
||||
return llvm::StringRef{name.data(), name.size(), true};
|
||||
}
|
||||
} // namespace wpi
|
||||
1182
wpiutil/src/main/native/cpp/support/json.cpp
Normal file
1182
wpiutil/src/main/native/cpp/support/json.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1233
wpiutil/src/main/native/cpp/support/json_binary_reader.cpp
Normal file
1233
wpiutil/src/main/native/cpp/support/json_binary_reader.cpp
Normal file
File diff suppressed because it is too large
Load Diff
668
wpiutil/src/main/native/cpp/support/json_binary_writer.cpp
Normal file
668
wpiutil/src/main/native/cpp/support/json_binary_writer.cpp
Normal file
@@ -0,0 +1,668 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Modifications Copyright (c) FIRST 2017. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++
|
||||
| | |__ | | | | | | version 2.1.1
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#define WPI_JSON_IMPLEMENTATION
|
||||
#include "support/json.h"
|
||||
|
||||
#include <array>
|
||||
#include <clocale> // lconv, localeconv
|
||||
#include <locale> // locale
|
||||
#include <numeric> // accumulate
|
||||
|
||||
#include "llvm/raw_ostream.h"
|
||||
#include "llvm/SmallString.h"
|
||||
#include "llvm/StringExtras.h"
|
||||
|
||||
using namespace wpi;
|
||||
|
||||
/*!
|
||||
@brief serialization to CBOR and MessagePack values
|
||||
*/
|
||||
class json::binary_writer
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
@brief create a binary writer
|
||||
|
||||
@param[in] adapter output adapter to write to
|
||||
*/
|
||||
explicit binary_writer(llvm::raw_ostream& s)
|
||||
: is_little_endian(little_endianess()), o(s)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief[in] j JSON value to serialize
|
||||
*/
|
||||
void write_cbor(const json& j);
|
||||
|
||||
/*!
|
||||
@brief[in] j JSON value to serialize
|
||||
*/
|
||||
void write_msgpack(const json& j);
|
||||
|
||||
/*!
|
||||
@brief determine system byte order
|
||||
|
||||
@return true iff system's byte order is little endian
|
||||
|
||||
@note from http://stackoverflow.com/a/1001328/266378
|
||||
*/
|
||||
static bool little_endianess() noexcept
|
||||
{
|
||||
int num = 1;
|
||||
return (*reinterpret_cast<char*>(&num) == 1);
|
||||
}
|
||||
|
||||
private:
|
||||
/*!
|
||||
@brief[in] str string to serialize
|
||||
*/
|
||||
void write_cbor_string(llvm::StringRef str);
|
||||
|
||||
/*!
|
||||
@brief[in] str string to serialize
|
||||
*/
|
||||
void write_msgpack_string(llvm::StringRef str);
|
||||
|
||||
/*
|
||||
@brief write a number to output input
|
||||
|
||||
@param[in] n number of type @a T
|
||||
@tparam T the type of the number
|
||||
|
||||
@note This function needs to respect the system's endianess, because
|
||||
bytes in CBOR and MessagePack are stored in network order (big
|
||||
endian) and therefore need reordering on little endian systems.
|
||||
*/
|
||||
template<typename T>
|
||||
void write_number(T n)
|
||||
{
|
||||
// step 1: write number to array of length T
|
||||
std::array<uint8_t, sizeof(T)> vec;
|
||||
std::memcpy(vec.data(), &n, sizeof(T));
|
||||
|
||||
// step 2: write array to output (with possible reordering)
|
||||
for (size_t i = 0; i < sizeof(T); ++i)
|
||||
{
|
||||
// reverse byte order prior to conversion if necessary
|
||||
if (is_little_endian)
|
||||
{
|
||||
o << static_cast<unsigned char>(vec[sizeof(T) - i - 1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
o << static_cast<unsigned char>(vec[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/// whether we can assume little endianess
|
||||
const bool is_little_endian = true;
|
||||
|
||||
/// the output
|
||||
llvm::raw_ostream& o;
|
||||
};
|
||||
|
||||
void json::binary_writer::write_cbor(const json& j)
|
||||
{
|
||||
switch (j.type())
|
||||
{
|
||||
case value_t::null:
|
||||
{
|
||||
o << static_cast<unsigned char>(0xf6);
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::boolean:
|
||||
{
|
||||
o << static_cast<unsigned char>(j.m_value.boolean ? 0xf5 : 0xf4);
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::number_integer:
|
||||
{
|
||||
if (j.m_value.number_integer >= 0)
|
||||
{
|
||||
// CBOR does not differentiate between positive signed
|
||||
// integers and unsigned integers. Therefore, we used the
|
||||
// code from the value_t::number_unsigned case here.
|
||||
if (j.m_value.number_integer <= 0x17)
|
||||
{
|
||||
write_number(static_cast<uint8_t>(j.m_value.number_integer));
|
||||
}
|
||||
else if (j.m_value.number_integer <= (std::numeric_limits<uint8_t>::max)())
|
||||
{
|
||||
o << static_cast<unsigned char>(0x18);
|
||||
write_number(static_cast<uint8_t>(j.m_value.number_integer));
|
||||
}
|
||||
else if (j.m_value.number_integer <= (std::numeric_limits<uint16_t>::max)())
|
||||
{
|
||||
o << static_cast<unsigned char>(0x19);
|
||||
write_number(static_cast<uint16_t>(j.m_value.number_integer));
|
||||
}
|
||||
else if (j.m_value.number_integer <= (std::numeric_limits<uint32_t>::max)())
|
||||
{
|
||||
o << static_cast<unsigned char>(0x1a);
|
||||
write_number(static_cast<uint32_t>(j.m_value.number_integer));
|
||||
}
|
||||
else
|
||||
{
|
||||
o << static_cast<unsigned char>(0x1b);
|
||||
write_number(static_cast<uint64_t>(j.m_value.number_integer));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The conversions below encode the sign in the first
|
||||
// byte, and the value is converted to a positive number.
|
||||
const auto positive_number = -1 - j.m_value.number_integer;
|
||||
if (j.m_value.number_integer >= -24)
|
||||
{
|
||||
write_number(static_cast<uint8_t>(0x20 + positive_number));
|
||||
}
|
||||
else if (positive_number <= (std::numeric_limits<uint8_t>::max)())
|
||||
{
|
||||
o << static_cast<unsigned char>(0x38);
|
||||
write_number(static_cast<uint8_t>(positive_number));
|
||||
}
|
||||
else if (positive_number <= (std::numeric_limits<uint16_t>::max)())
|
||||
{
|
||||
o << static_cast<unsigned char>(0x39);
|
||||
write_number(static_cast<uint16_t>(positive_number));
|
||||
}
|
||||
else if (positive_number <= (std::numeric_limits<uint32_t>::max)())
|
||||
{
|
||||
o << static_cast<unsigned char>(0x3a);
|
||||
write_number(static_cast<uint32_t>(positive_number));
|
||||
}
|
||||
else
|
||||
{
|
||||
o << static_cast<unsigned char>(0x3b);
|
||||
write_number(static_cast<uint64_t>(positive_number));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::number_unsigned:
|
||||
{
|
||||
if (j.m_value.number_unsigned <= 0x17)
|
||||
{
|
||||
write_number(static_cast<uint8_t>(j.m_value.number_unsigned));
|
||||
}
|
||||
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
|
||||
{
|
||||
o << static_cast<unsigned char>(0x18);
|
||||
write_number(static_cast<uint8_t>(j.m_value.number_unsigned));
|
||||
}
|
||||
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)())
|
||||
{
|
||||
o << static_cast<unsigned char>(0x19);
|
||||
write_number(static_cast<uint16_t>(j.m_value.number_unsigned));
|
||||
}
|
||||
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)())
|
||||
{
|
||||
o << static_cast<unsigned char>(0x1a);
|
||||
write_number(static_cast<uint32_t>(j.m_value.number_unsigned));
|
||||
}
|
||||
else
|
||||
{
|
||||
o << static_cast<unsigned char>(0x1b);
|
||||
write_number(static_cast<uint64_t>(j.m_value.number_unsigned));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::number_float:
|
||||
{
|
||||
// Double-Precision Float
|
||||
o << static_cast<unsigned char>(0xfb);
|
||||
write_number(j.m_value.number_float);
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::string:
|
||||
{
|
||||
write_cbor_string(*j.m_value.string);
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::array:
|
||||
{
|
||||
// step 1: write control byte and the array size
|
||||
const auto N = j.m_value.array->size();
|
||||
if (N <= 0x17)
|
||||
{
|
||||
write_number(static_cast<uint8_t>(0x80 + N));
|
||||
}
|
||||
else if (N <= 0xff)
|
||||
{
|
||||
o << static_cast<unsigned char>(0x98);
|
||||
write_number(static_cast<uint8_t>(N));
|
||||
}
|
||||
else if (N <= 0xffff)
|
||||
{
|
||||
o << static_cast<unsigned char>(0x99);
|
||||
write_number(static_cast<uint16_t>(N));
|
||||
}
|
||||
else if (N <= 0xffffffff)
|
||||
{
|
||||
o << static_cast<unsigned char>(0x9a);
|
||||
write_number(static_cast<uint32_t>(N));
|
||||
}
|
||||
// LCOV_EXCL_START
|
||||
else if (N <= 0xffffffffffffffff)
|
||||
{
|
||||
o << static_cast<unsigned char>(0x9b);
|
||||
write_number(static_cast<uint64_t>(N));
|
||||
}
|
||||
// LCOV_EXCL_STOP
|
||||
|
||||
// step 2: write each element
|
||||
for (const auto& el : *j.m_value.array)
|
||||
{
|
||||
write_cbor(el);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::object:
|
||||
{
|
||||
// step 1: write control byte and the object size
|
||||
const auto N = j.m_value.object->size();
|
||||
if (N <= 0x17)
|
||||
{
|
||||
write_number(static_cast<uint8_t>(0xa0 + N));
|
||||
}
|
||||
else if (N <= 0xff)
|
||||
{
|
||||
o << static_cast<unsigned char>(0xb8);
|
||||
write_number(static_cast<uint8_t>(N));
|
||||
}
|
||||
else if (N <= 0xffff)
|
||||
{
|
||||
o << static_cast<unsigned char>(0xb9);
|
||||
write_number(static_cast<uint16_t>(N));
|
||||
}
|
||||
else if (N <= 0xffffffff)
|
||||
{
|
||||
o << static_cast<unsigned char>(0xba);
|
||||
write_number(static_cast<uint32_t>(N));
|
||||
}
|
||||
// LCOV_EXCL_START
|
||||
else if (N <= 0xffffffffffffffff)
|
||||
{
|
||||
o << static_cast<unsigned char>(0xbb);
|
||||
write_number(static_cast<uint64_t>(N));
|
||||
}
|
||||
// LCOV_EXCL_STOP
|
||||
|
||||
// step 2: write each element
|
||||
for (const auto& el : *j.m_value.object)
|
||||
{
|
||||
write_cbor_string(el.first());
|
||||
write_cbor(el.second);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void json::binary_writer::write_cbor_string(llvm::StringRef str)
|
||||
{
|
||||
// step 1: write control byte and the string length
|
||||
const auto N = str.size();
|
||||
if (N <= 0x17)
|
||||
{
|
||||
write_number(static_cast<uint8_t>(0x60 + N));
|
||||
}
|
||||
else if (N <= 0xff)
|
||||
{
|
||||
o << static_cast<unsigned char>(0x78);
|
||||
write_number(static_cast<uint8_t>(N));
|
||||
}
|
||||
else if (N <= 0xffff)
|
||||
{
|
||||
o << static_cast<unsigned char>(0x79);
|
||||
write_number(static_cast<uint16_t>(N));
|
||||
}
|
||||
else if (N <= 0xffffffff)
|
||||
{
|
||||
o << static_cast<unsigned char>(0x7a);
|
||||
write_number(static_cast<uint32_t>(N));
|
||||
}
|
||||
// LCOV_EXCL_START
|
||||
else if (N <= 0xffffffffffffffff)
|
||||
{
|
||||
o << static_cast<unsigned char>(0x7b);
|
||||
write_number(static_cast<uint64_t>(N));
|
||||
}
|
||||
// LCOV_EXCL_STOP
|
||||
|
||||
// step 2: write the string
|
||||
o << str;
|
||||
}
|
||||
|
||||
void json::binary_writer::write_msgpack(const json& j)
|
||||
{
|
||||
switch (j.type())
|
||||
{
|
||||
case value_t::null:
|
||||
{
|
||||
// nil
|
||||
o << static_cast<unsigned char>(0xc0);
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::boolean:
|
||||
{
|
||||
// true and false
|
||||
o << static_cast<unsigned char>(j.m_value.boolean ? 0xc3 : 0xc2);
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::number_integer:
|
||||
{
|
||||
if (j.m_value.number_integer >= 0)
|
||||
{
|
||||
// MessagePack does not differentiate between positive
|
||||
// signed integers and unsigned integers. Therefore, we
|
||||
// used the code from the value_t::number_unsigned case
|
||||
// here.
|
||||
if (j.m_value.number_unsigned < 128)
|
||||
{
|
||||
// positive fixnum
|
||||
write_number(static_cast<uint8_t>(j.m_value.number_integer));
|
||||
}
|
||||
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
|
||||
{
|
||||
// uint 8
|
||||
o << static_cast<unsigned char>(0xcc);
|
||||
write_number(static_cast<uint8_t>(j.m_value.number_integer));
|
||||
}
|
||||
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)())
|
||||
{
|
||||
// uint 16
|
||||
o << static_cast<unsigned char>(0xcd);
|
||||
write_number(static_cast<uint16_t>(j.m_value.number_integer));
|
||||
}
|
||||
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)())
|
||||
{
|
||||
// uint 32
|
||||
o << static_cast<unsigned char>(0xce);
|
||||
write_number(static_cast<uint32_t>(j.m_value.number_integer));
|
||||
}
|
||||
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint64_t>::max)())
|
||||
{
|
||||
// uint 64
|
||||
o << static_cast<unsigned char>(0xcf);
|
||||
write_number(static_cast<uint64_t>(j.m_value.number_integer));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (j.m_value.number_integer >= -32)
|
||||
{
|
||||
// negative fixnum
|
||||
write_number(static_cast<int8_t>(j.m_value.number_integer));
|
||||
}
|
||||
else if (j.m_value.number_integer >= (std::numeric_limits<int8_t>::min)() && j.m_value.number_integer <= (std::numeric_limits<int8_t>::max)())
|
||||
{
|
||||
// int 8
|
||||
o << static_cast<unsigned char>(0xd0);
|
||||
write_number(static_cast<int8_t>(j.m_value.number_integer));
|
||||
}
|
||||
else if (j.m_value.number_integer >= (std::numeric_limits<int16_t>::min)() && j.m_value.number_integer <= (std::numeric_limits<int16_t>::max)())
|
||||
{
|
||||
// int 16
|
||||
o << static_cast<unsigned char>(0xd1);
|
||||
write_number(static_cast<int16_t>(j.m_value.number_integer));
|
||||
}
|
||||
else if (j.m_value.number_integer >= (std::numeric_limits<int32_t>::min)() && j.m_value.number_integer <= (std::numeric_limits<int32_t>::max)())
|
||||
{
|
||||
// int 32
|
||||
o << static_cast<unsigned char>(0xd2);
|
||||
write_number(static_cast<int32_t>(j.m_value.number_integer));
|
||||
}
|
||||
else if (j.m_value.number_integer >= (std::numeric_limits<int64_t>::min)() && j.m_value.number_integer <= (std::numeric_limits<int64_t>::max)())
|
||||
{
|
||||
// int 64
|
||||
o << static_cast<unsigned char>(0xd3);
|
||||
write_number(static_cast<int64_t>(j.m_value.number_integer));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::number_unsigned:
|
||||
{
|
||||
if (j.m_value.number_unsigned < 128)
|
||||
{
|
||||
// positive fixnum
|
||||
write_number(static_cast<uint8_t>(j.m_value.number_integer));
|
||||
}
|
||||
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())
|
||||
{
|
||||
// uint 8
|
||||
o << static_cast<unsigned char>(0xcc);
|
||||
write_number(static_cast<uint8_t>(j.m_value.number_integer));
|
||||
}
|
||||
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)())
|
||||
{
|
||||
// uint 16
|
||||
o << static_cast<unsigned char>(0xcd);
|
||||
write_number(static_cast<uint16_t>(j.m_value.number_integer));
|
||||
}
|
||||
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)())
|
||||
{
|
||||
// uint 32
|
||||
o << static_cast<unsigned char>(0xce);
|
||||
write_number(static_cast<uint32_t>(j.m_value.number_integer));
|
||||
}
|
||||
else if (j.m_value.number_unsigned <= (std::numeric_limits<uint64_t>::max)())
|
||||
{
|
||||
// uint 64
|
||||
o << static_cast<unsigned char>(0xcf);
|
||||
write_number(static_cast<uint64_t>(j.m_value.number_integer));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::number_float:
|
||||
{
|
||||
// float 64
|
||||
o << static_cast<unsigned char>(0xcb);
|
||||
write_number(j.m_value.number_float);
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::string:
|
||||
{
|
||||
write_msgpack_string(*j.m_value.string);
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::array:
|
||||
{
|
||||
// step 1: write control byte and the array size
|
||||
const auto N = j.m_value.array->size();
|
||||
if (N <= 15)
|
||||
{
|
||||
// fixarray
|
||||
write_number(static_cast<uint8_t>(0x90 | N));
|
||||
}
|
||||
else if (N <= 0xffff)
|
||||
{
|
||||
// array 16
|
||||
o << static_cast<unsigned char>(0xdc);
|
||||
write_number(static_cast<uint16_t>(N));
|
||||
}
|
||||
else if (N <= 0xffffffff)
|
||||
{
|
||||
// array 32
|
||||
o << static_cast<unsigned char>(0xdd);
|
||||
write_number(static_cast<uint32_t>(N));
|
||||
}
|
||||
|
||||
// step 2: write each element
|
||||
for (const auto& el : *j.m_value.array)
|
||||
{
|
||||
write_msgpack(el);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::object:
|
||||
{
|
||||
// step 1: write control byte and the object size
|
||||
const auto N = j.m_value.object->size();
|
||||
if (N <= 15)
|
||||
{
|
||||
// fixmap
|
||||
write_number(static_cast<uint8_t>(0x80 | (N & 0xf)));
|
||||
}
|
||||
else if (N <= 65535)
|
||||
{
|
||||
// map 16
|
||||
o << static_cast<unsigned char>(0xde);
|
||||
write_number(static_cast<uint16_t>(N));
|
||||
}
|
||||
else if (N <= 4294967295)
|
||||
{
|
||||
// map 32
|
||||
o << static_cast<unsigned char>(0xdf);
|
||||
write_number(static_cast<uint32_t>(N));
|
||||
}
|
||||
|
||||
// step 2: write each element
|
||||
for (const auto& el : *j.m_value.object)
|
||||
{
|
||||
write_msgpack_string(el.first());
|
||||
write_msgpack(el.second);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void json::binary_writer::write_msgpack_string(llvm::StringRef str)
|
||||
{
|
||||
// step 1: write control byte and the string length
|
||||
const auto N = str.size();
|
||||
if (N <= 31)
|
||||
{
|
||||
// fixstr
|
||||
write_number(static_cast<uint8_t>(0xa0 | N));
|
||||
}
|
||||
else if (N <= 255)
|
||||
{
|
||||
// str 8
|
||||
o << static_cast<unsigned char>(0xd9);
|
||||
write_number(static_cast<uint8_t>(N));
|
||||
}
|
||||
else if (N <= 65535)
|
||||
{
|
||||
// str 16
|
||||
o << static_cast<unsigned char>(0xda);
|
||||
write_number(static_cast<uint16_t>(N));
|
||||
}
|
||||
else if (N <= 4294967295)
|
||||
{
|
||||
// str 32
|
||||
o << static_cast<unsigned char>(0xdb);
|
||||
write_number(static_cast<uint32_t>(N));
|
||||
}
|
||||
|
||||
// step 2: write the string
|
||||
o << str;
|
||||
}
|
||||
|
||||
void json::to_cbor(llvm::raw_ostream& os, const json& j)
|
||||
{
|
||||
binary_writer bw(os);
|
||||
bw.write_cbor(j);
|
||||
}
|
||||
|
||||
llvm::StringRef json::to_cbor(const json& j, llvm::SmallVectorImpl<char> buf)
|
||||
{
|
||||
llvm::raw_svector_ostream os(buf);
|
||||
binary_writer bw(os);
|
||||
bw.write_cbor(j);
|
||||
return os.str();
|
||||
}
|
||||
|
||||
std::string json::to_cbor(const json& j)
|
||||
{
|
||||
std::string s;
|
||||
llvm::raw_string_ostream os(s);
|
||||
binary_writer bw(os);
|
||||
bw.write_cbor(j);
|
||||
os.flush();
|
||||
return s;
|
||||
}
|
||||
|
||||
void json::to_msgpack(llvm::raw_ostream& os, const json& j)
|
||||
{
|
||||
binary_writer bw(os);
|
||||
bw.write_msgpack(j);
|
||||
}
|
||||
|
||||
llvm::StringRef json::to_msgpack(const json& j, llvm::SmallVectorImpl<char> buf)
|
||||
{
|
||||
llvm::raw_svector_ostream os(buf);
|
||||
binary_writer bw(os);
|
||||
bw.write_msgpack(j);
|
||||
return os.str();
|
||||
}
|
||||
|
||||
std::string json::to_msgpack(const json& j)
|
||||
{
|
||||
std::string s;
|
||||
llvm::raw_string_ostream os(s);
|
||||
binary_writer bw(os);
|
||||
bw.write_msgpack(j);
|
||||
os.flush();
|
||||
return s;
|
||||
}
|
||||
2095
wpiutil/src/main/native/cpp/support/json_parser.cpp
Normal file
2095
wpiutil/src/main/native/cpp/support/json_parser.cpp
Normal file
File diff suppressed because it is too large
Load Diff
540
wpiutil/src/main/native/cpp/support/json_pointer.cpp
Normal file
540
wpiutil/src/main/native/cpp/support/json_pointer.cpp
Normal file
@@ -0,0 +1,540 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Modifications Copyright (c) FIRST 2017. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++
|
||||
| | |__ | | | | | | version 2.1.1
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#define WPI_JSON_IMPLEMENTATION
|
||||
#include "support/json.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric> // accumulate
|
||||
|
||||
using namespace wpi;
|
||||
|
||||
std::string json::json_pointer::to_string() const noexcept
|
||||
{
|
||||
return std::accumulate(reference_tokens.begin(),
|
||||
reference_tokens.end(), std::string{},
|
||||
[](const std::string & a, const std::string & b)
|
||||
{
|
||||
return a + "/" + escape(b);
|
||||
});
|
||||
}
|
||||
|
||||
json::reference json::json_pointer::get_and_create(reference j) const
|
||||
{
|
||||
pointer result = &j;
|
||||
|
||||
// in case no reference tokens exist, return a reference to the
|
||||
// JSON value j which will be overwritten by a primitive value
|
||||
for (const auto& reference_token : reference_tokens)
|
||||
{
|
||||
switch (result->m_type)
|
||||
{
|
||||
case value_t::null:
|
||||
{
|
||||
if (reference_token == "0")
|
||||
{
|
||||
// start a new array if reference token is 0
|
||||
result = &result->operator[](0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// start a new object otherwise
|
||||
result = &result->operator[](reference_token);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::object:
|
||||
{
|
||||
// create an entry in the object
|
||||
result = &result->operator[](reference_token);
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::array:
|
||||
{
|
||||
// create an entry in the array
|
||||
JSON_TRY
|
||||
{
|
||||
result = &result->operator[](static_cast<size_type>(std::stoi(reference_token)));
|
||||
}
|
||||
JSON_CATCH (std::invalid_argument&)
|
||||
{
|
||||
JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
The following code is only reached if there exists a
|
||||
reference token _and_ the current value is primitive. In
|
||||
this case, we have an error situation, because primitive
|
||||
values may only occur as single value; that is, with an
|
||||
empty list of reference tokens.
|
||||
*/
|
||||
default:
|
||||
{
|
||||
JSON_THROW(type_error::create(313, "invalid value to unflatten"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *result;
|
||||
}
|
||||
|
||||
json::reference json::json_pointer::get_unchecked(pointer ptr) const
|
||||
{
|
||||
for (const auto& reference_token : reference_tokens)
|
||||
{
|
||||
// convert null values to arrays or objects before continuing
|
||||
if (ptr->m_type == value_t::null)
|
||||
{
|
||||
// check if reference token is a number
|
||||
const bool nums = std::all_of(reference_token.begin(),
|
||||
reference_token.end(),
|
||||
[](const char x)
|
||||
{
|
||||
return (x >= '0' && x <= '9');
|
||||
});
|
||||
|
||||
// change value to array for numbers or "-" or to object
|
||||
// otherwise
|
||||
if (nums || reference_token == "-")
|
||||
{
|
||||
*ptr = value_t::array;
|
||||
}
|
||||
else
|
||||
{
|
||||
*ptr = value_t::object;
|
||||
}
|
||||
}
|
||||
|
||||
switch (ptr->m_type)
|
||||
{
|
||||
case value_t::object:
|
||||
{
|
||||
// use unchecked object access
|
||||
ptr = &ptr->operator[](reference_token);
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::array:
|
||||
{
|
||||
// error condition (cf. RFC 6901, Sect. 4)
|
||||
if (reference_token.size() > 1 && reference_token[0] == '0')
|
||||
{
|
||||
JSON_THROW(parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'"));
|
||||
}
|
||||
|
||||
if (reference_token == "-")
|
||||
{
|
||||
// explicitly treat "-" as index beyond the end
|
||||
ptr = &ptr->operator[](ptr->m_value.array->size());
|
||||
}
|
||||
else
|
||||
{
|
||||
// convert array index to number; unchecked access
|
||||
JSON_TRY
|
||||
{
|
||||
ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token)));
|
||||
}
|
||||
JSON_CATCH (std::invalid_argument&)
|
||||
{
|
||||
JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
JSON_THROW(out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
json::reference json::json_pointer::get_checked(pointer ptr) const
|
||||
{
|
||||
for (const auto& reference_token : reference_tokens)
|
||||
{
|
||||
switch (ptr->m_type)
|
||||
{
|
||||
case value_t::object:
|
||||
{
|
||||
// note: at performs range check
|
||||
ptr = &ptr->at(reference_token);
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::array:
|
||||
{
|
||||
if (reference_token == "-")
|
||||
{
|
||||
// "-" always fails the range check
|
||||
JSON_THROW(out_of_range::create(402, "array index '-' (" +
|
||||
std::to_string(ptr->m_value.array->size()) +
|
||||
") is out of range"));
|
||||
}
|
||||
|
||||
// error condition (cf. RFC 6901, Sect. 4)
|
||||
if (reference_token.size() > 1 && reference_token[0] == '0')
|
||||
{
|
||||
JSON_THROW(parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'"));
|
||||
}
|
||||
|
||||
// note: at performs range check
|
||||
JSON_TRY
|
||||
{
|
||||
ptr = &ptr->at(static_cast<size_type>(std::stoi(reference_token)));
|
||||
}
|
||||
JSON_CATCH (std::invalid_argument&)
|
||||
{
|
||||
JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
JSON_THROW(out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
json::const_reference json::json_pointer::get_unchecked(const_pointer ptr) const
|
||||
{
|
||||
for (const auto& reference_token : reference_tokens)
|
||||
{
|
||||
switch (ptr->m_type)
|
||||
{
|
||||
case value_t::object:
|
||||
{
|
||||
// use unchecked object access
|
||||
ptr = &ptr->operator[](reference_token);
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::array:
|
||||
{
|
||||
if (reference_token == "-")
|
||||
{
|
||||
// "-" cannot be used for const access
|
||||
JSON_THROW(out_of_range::create(402, "array index '-' (" +
|
||||
std::to_string(ptr->m_value.array->size()) +
|
||||
") is out of range"));
|
||||
}
|
||||
|
||||
// error condition (cf. RFC 6901, Sect. 4)
|
||||
if (reference_token.size() > 1 && reference_token[0] == '0')
|
||||
{
|
||||
JSON_THROW(parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'"));
|
||||
}
|
||||
|
||||
// use unchecked array access
|
||||
JSON_TRY
|
||||
{
|
||||
ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token)));
|
||||
}
|
||||
JSON_CATCH (std::invalid_argument&)
|
||||
{
|
||||
JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
JSON_THROW(out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
json::const_reference json::json_pointer::get_checked(const_pointer ptr) const
|
||||
{
|
||||
for (const auto& reference_token : reference_tokens)
|
||||
{
|
||||
switch (ptr->m_type)
|
||||
{
|
||||
case value_t::object:
|
||||
{
|
||||
// note: at performs range check
|
||||
ptr = &ptr->at(reference_token);
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::array:
|
||||
{
|
||||
if (reference_token == "-")
|
||||
{
|
||||
// "-" always fails the range check
|
||||
JSON_THROW(out_of_range::create(402, "array index '-' (" +
|
||||
std::to_string(ptr->m_value.array->size()) +
|
||||
") is out of range"));
|
||||
}
|
||||
|
||||
// error condition (cf. RFC 6901, Sect. 4)
|
||||
if (reference_token.size() > 1 && reference_token[0] == '0')
|
||||
{
|
||||
JSON_THROW(parse_error::create(106, 0, "array index '" + reference_token + "' must not begin with '0'"));
|
||||
}
|
||||
|
||||
// note: at performs range check
|
||||
JSON_TRY
|
||||
{
|
||||
ptr = &ptr->at(static_cast<size_type>(std::stoi(reference_token)));
|
||||
}
|
||||
JSON_CATCH (std::invalid_argument&)
|
||||
{
|
||||
JSON_THROW(parse_error::create(109, 0, "array index '" + reference_token + "' is not a number"));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
JSON_THROW(out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
std::vector<std::string> json::json_pointer::split(const std::string& reference_string)
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
|
||||
// special case: empty reference string -> no reference tokens
|
||||
if (reference_string.empty())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
// check if nonempty reference string begins with slash
|
||||
if (reference_string[0] != '/')
|
||||
{
|
||||
JSON_THROW(parse_error::create(107, 1, "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'"));
|
||||
}
|
||||
|
||||
// extract the reference tokens:
|
||||
// - slash: position of the last read slash (or end of string)
|
||||
// - start: position after the previous slash
|
||||
for (
|
||||
// search for the first slash after the first character
|
||||
size_t slash = reference_string.find_first_of('/', 1),
|
||||
// set the beginning of the first reference token
|
||||
start = 1;
|
||||
// we can stop if start == string::npos+1 = 0
|
||||
start != 0;
|
||||
// set the beginning of the next reference token
|
||||
// (will eventually be 0 if slash == std::string::npos)
|
||||
start = slash + 1,
|
||||
// find next slash
|
||||
slash = reference_string.find_first_of('/', start))
|
||||
{
|
||||
// use the text between the beginning of the reference token
|
||||
// (start) and the last slash (slash).
|
||||
auto reference_token = reference_string.substr(start, slash - start);
|
||||
|
||||
// check reference tokens are properly escaped
|
||||
for (size_t pos = reference_token.find_first_of('~');
|
||||
pos != std::string::npos;
|
||||
pos = reference_token.find_first_of('~', pos + 1))
|
||||
{
|
||||
assert(reference_token[pos] == '~');
|
||||
|
||||
// ~ must be followed by 0 or 1
|
||||
if (pos == reference_token.size() - 1 ||
|
||||
(reference_token[pos + 1] != '0' &&
|
||||
reference_token[pos + 1] != '1'))
|
||||
{
|
||||
JSON_THROW(parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'"));
|
||||
}
|
||||
}
|
||||
|
||||
// finally, store the reference token
|
||||
unescape(reference_token);
|
||||
result.push_back(reference_token);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief replace all occurrences of a substring by another string
|
||||
|
||||
@param[in,out] s the string to manipulate; changed so that all
|
||||
occurrences of @a f are replaced with @a t
|
||||
@param[in] f the substring to replace with @a t
|
||||
@param[in] t the string to replace @a f
|
||||
|
||||
@pre The search string @a f must not be empty. **This precondition is
|
||||
enforced with an assertion.**
|
||||
|
||||
@since version 2.0.0
|
||||
*/
|
||||
void json::json_pointer::replace_substring(std::string& s,
|
||||
const std::string& f,
|
||||
const std::string& t)
|
||||
{
|
||||
assert(!f.empty());
|
||||
|
||||
for (
|
||||
size_t pos = s.find(f); // find first occurrence of f
|
||||
pos != std::string::npos; // make sure f was found
|
||||
s.replace(pos, f.size(), t), // replace with t
|
||||
pos = s.find(f, pos + t.size()) // find next occurrence of f
|
||||
);
|
||||
}
|
||||
|
||||
/// escape tilde and slash
|
||||
std::string json::json_pointer::escape(std::string s)
|
||||
{
|
||||
// escape "~"" to "~0" and "/" to "~1"
|
||||
replace_substring(s, "~", "~0");
|
||||
replace_substring(s, "/", "~1");
|
||||
return s;
|
||||
}
|
||||
|
||||
/// unescape tilde and slash
|
||||
void json::json_pointer::unescape(std::string& s)
|
||||
{
|
||||
// first transform any occurrence of the sequence '~1' to '/'
|
||||
replace_substring(s, "~1", "/");
|
||||
// then transform any occurrence of the sequence '~0' to '~'
|
||||
replace_substring(s, "~0", "~");
|
||||
}
|
||||
|
||||
void json::json_pointer::flatten(const std::string& reference_string,
|
||||
const json& value,
|
||||
json& result)
|
||||
{
|
||||
switch (value.m_type)
|
||||
{
|
||||
case value_t::array:
|
||||
{
|
||||
if (value.m_value.array->empty())
|
||||
{
|
||||
// flatten empty array as null
|
||||
result[reference_string] = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
// iterate array and use index as reference string
|
||||
for (size_t i = 0; i < value.m_value.array->size(); ++i)
|
||||
{
|
||||
flatten(reference_string + "/" + std::to_string(i),
|
||||
value.m_value.array->operator[](i), result);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case value_t::object:
|
||||
{
|
||||
if (value.m_value.object->empty())
|
||||
{
|
||||
// flatten empty object as null
|
||||
result[reference_string] = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
// iterate object and use keys as reference string
|
||||
for (const auto& element : *value.m_value.object)
|
||||
{
|
||||
flatten(reference_string + "/" + escape(element.first()),
|
||||
element.second, result);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
// add primitive value with its reference string
|
||||
result[reference_string] = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
json json::json_pointer::unflatten(const json& value)
|
||||
{
|
||||
if (!value.is_object())
|
||||
{
|
||||
JSON_THROW(type_error::create(314, "only objects can be unflattened"));
|
||||
}
|
||||
|
||||
// we need to iterate over the object values in sorted key order
|
||||
llvm::SmallVector<llvm::StringMapConstIterator<json>, 64> sorted;
|
||||
for (auto i = value.m_value.object->begin(),
|
||||
end = value.m_value.object->end(); i != end; ++i)
|
||||
{
|
||||
if (!i->second.is_primitive())
|
||||
{
|
||||
JSON_THROW(type_error::create(315, "values in object must be primitive"));
|
||||
}
|
||||
sorted.push_back(i);
|
||||
}
|
||||
std::sort(sorted.begin(), sorted.end(),
|
||||
[](const llvm::StringMapConstIterator<json>& a,
|
||||
const llvm::StringMapConstIterator<json>& b) {
|
||||
return a->getKey() < b->getKey();
|
||||
});
|
||||
|
||||
json result;
|
||||
|
||||
// iterate the sorted JSON object values
|
||||
for (const auto& element : sorted)
|
||||
{
|
||||
|
||||
// assign value to reference pointed to by JSON pointer; Note
|
||||
// that if the JSON pointer is "" (i.e., points to the whole
|
||||
// value), function get_and_create returns a reference to
|
||||
// result itself. An assignment will then create a primitive
|
||||
// value.
|
||||
json_pointer(element->first()).get_and_create(result) = element->second;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
433
wpiutil/src/main/native/cpp/support/json_serializer.cpp
Normal file
433
wpiutil/src/main/native/cpp/support/json_serializer.cpp
Normal file
@@ -0,0 +1,433 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Modifications Copyright (c) FIRST 2017. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++
|
||||
| | |__ | | | | | | version 2.1.1
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#define WPI_JSON_IMPLEMENTATION
|
||||
#include "support/json.h"
|
||||
|
||||
#include "llvm/SmallString.h"
|
||||
#include "llvm/StringExtras.h"
|
||||
|
||||
#include "json_serializer.h"
|
||||
|
||||
using namespace wpi;
|
||||
|
||||
void json::serializer::dump(const json& val,
|
||||
const bool pretty_print,
|
||||
const unsigned int indent_step,
|
||||
const unsigned int current_indent)
|
||||
{
|
||||
switch (val.m_type)
|
||||
{
|
||||
case value_t::object:
|
||||
{
|
||||
if (val.m_value.object->empty())
|
||||
{
|
||||
o << "{}";
|
||||
return;
|
||||
}
|
||||
|
||||
if (pretty_print)
|
||||
{
|
||||
o << "{\n";
|
||||
|
||||
// variable to hold indentation for recursive calls
|
||||
const auto new_indent = current_indent + indent_step;
|
||||
|
||||
// first n-1 elements
|
||||
auto i = val.m_value.object->begin();
|
||||
for (size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
|
||||
{
|
||||
o.indent(new_indent);
|
||||
o << '\"';
|
||||
dump_escaped(i->first());
|
||||
o << "\": ";
|
||||
dump(i->second, true, indent_step, new_indent);
|
||||
o << ",\n";
|
||||
}
|
||||
|
||||
// last element
|
||||
assert(i != val.m_value.object->end());
|
||||
o.indent(new_indent);
|
||||
o << '\"';
|
||||
dump_escaped(i->first());
|
||||
o << "\": ";
|
||||
dump(i->second, true, indent_step, new_indent);
|
||||
|
||||
o << '\n';
|
||||
o.indent(current_indent);
|
||||
o << '}';
|
||||
}
|
||||
else
|
||||
{
|
||||
o << '{';
|
||||
|
||||
// first n-1 elements
|
||||
auto i = val.m_value.object->begin();
|
||||
for (size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)
|
||||
{
|
||||
o << '\"';
|
||||
dump_escaped(i->first());
|
||||
o << "\":";
|
||||
dump(i->second, false, indent_step, current_indent);
|
||||
o << ',';
|
||||
}
|
||||
|
||||
// last element
|
||||
assert(i != val.m_value.object->end());
|
||||
o << '\"';
|
||||
dump_escaped(i->first());
|
||||
o << "\":";
|
||||
dump(i->second, false, indent_step, current_indent);
|
||||
|
||||
o << '}';
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::array:
|
||||
{
|
||||
if (val.m_value.array->empty())
|
||||
{
|
||||
o << "[]";
|
||||
return;
|
||||
}
|
||||
|
||||
if (pretty_print)
|
||||
{
|
||||
o << "[\n";
|
||||
|
||||
// variable to hold indentation for recursive calls
|
||||
const auto new_indent = current_indent + indent_step;
|
||||
|
||||
// first n-1 elements
|
||||
for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i)
|
||||
{
|
||||
o.indent(new_indent);
|
||||
dump(*i, true, indent_step, new_indent);
|
||||
o << ",\n";
|
||||
}
|
||||
|
||||
// last element
|
||||
assert(!val.m_value.array->empty());
|
||||
o.indent(new_indent);
|
||||
dump(val.m_value.array->back(), true, indent_step, new_indent);
|
||||
|
||||
o << '\n';
|
||||
o.indent(current_indent);
|
||||
o << ']';
|
||||
}
|
||||
else
|
||||
{
|
||||
o << '[';
|
||||
|
||||
// first n-1 elements
|
||||
for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i)
|
||||
{
|
||||
dump(*i, false, indent_step, current_indent);
|
||||
o << ',';
|
||||
}
|
||||
|
||||
// last element
|
||||
assert(!val.m_value.array->empty());
|
||||
dump(val.m_value.array->back(), false, indent_step, current_indent);
|
||||
|
||||
o << ']';
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::string:
|
||||
{
|
||||
o << '\"';
|
||||
dump_escaped(*val.m_value.string);
|
||||
o << '\"';
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::boolean:
|
||||
{
|
||||
if (val.m_value.boolean)
|
||||
{
|
||||
o << "true";
|
||||
}
|
||||
else
|
||||
{
|
||||
o << "false";
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::number_integer:
|
||||
{
|
||||
o << static_cast<long long>(val.m_value.number_integer);
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::number_unsigned:
|
||||
{
|
||||
o << static_cast<unsigned long long>(val.m_value.number_unsigned);
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::number_float:
|
||||
{
|
||||
dump_float(val.m_value.number_float);
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::discarded:
|
||||
{
|
||||
o << "<discarded>";
|
||||
return;
|
||||
}
|
||||
|
||||
case value_t::null:
|
||||
{
|
||||
o << "null";
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void json::serializer::dump_escaped(llvm::StringRef s) const
|
||||
{
|
||||
for (const auto& c : s)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
// quotation mark (0x22)
|
||||
case '"':
|
||||
{
|
||||
o << '\\' << '"';
|
||||
break;
|
||||
}
|
||||
|
||||
// reverse solidus (0x5c)
|
||||
case '\\':
|
||||
{
|
||||
// nothing to change
|
||||
o << '\\' << '\\';
|
||||
break;
|
||||
}
|
||||
|
||||
// backspace (0x08)
|
||||
case '\b':
|
||||
{
|
||||
o << '\\' << 'b';
|
||||
break;
|
||||
}
|
||||
|
||||
// formfeed (0x0c)
|
||||
case '\f':
|
||||
{
|
||||
o << '\\' << 'f';
|
||||
break;
|
||||
}
|
||||
|
||||
// newline (0x0a)
|
||||
case '\n':
|
||||
{
|
||||
o << '\\' << 'n';
|
||||
break;
|
||||
}
|
||||
|
||||
// carriage return (0x0d)
|
||||
case '\r':
|
||||
{
|
||||
o << '\\' << 'r';
|
||||
break;
|
||||
}
|
||||
|
||||
// horizontal tab (0x09)
|
||||
case '\t':
|
||||
{
|
||||
o << '\\' << 't';
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x00:
|
||||
case 0x01:
|
||||
case 0x02:
|
||||
case 0x03:
|
||||
case 0x04:
|
||||
case 0x05:
|
||||
case 0x06:
|
||||
case 0x07:
|
||||
case 0x0b:
|
||||
case 0x0e:
|
||||
case 0x0f:
|
||||
case 0x10:
|
||||
case 0x11:
|
||||
case 0x12:
|
||||
case 0x13:
|
||||
case 0x14:
|
||||
case 0x15:
|
||||
case 0x16:
|
||||
case 0x17:
|
||||
case 0x18:
|
||||
case 0x19:
|
||||
case 0x1a:
|
||||
case 0x1b:
|
||||
case 0x1c:
|
||||
case 0x1d:
|
||||
case 0x1e:
|
||||
case 0x1f:
|
||||
{
|
||||
// print character c as \uxxxx
|
||||
o << "\\u00";
|
||||
o << llvm::hexdigit((c >> 4) & 0xf, true);
|
||||
o << llvm::hexdigit((c >> 0) & 0xf, true);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
// all other characters are added as-is
|
||||
o << c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void json::serializer::dump_float(double x)
|
||||
{
|
||||
// NaN / inf
|
||||
if (!std::isfinite(x) || std::isnan(x))
|
||||
{
|
||||
o << "null";
|
||||
return;
|
||||
}
|
||||
|
||||
// special case for 0.0 and -0.0
|
||||
if (x == 0)
|
||||
{
|
||||
if (std::signbit(x))
|
||||
{
|
||||
o << "-0.0";
|
||||
}
|
||||
else
|
||||
{
|
||||
o << "0.0";
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// get number of digits for a text -> float -> text round-trip
|
||||
static constexpr auto d = std::numeric_limits<double>::digits10;
|
||||
|
||||
// the actual conversion
|
||||
llvm::SmallString<64> number_buffer;
|
||||
number_buffer.resize(64);
|
||||
std::ptrdiff_t len = snprintf(number_buffer.data(), number_buffer.size(),
|
||||
"%.*g", d, x);
|
||||
|
||||
// negative value indicates an error
|
||||
assert(len > 0);
|
||||
// check if buffer was large enough
|
||||
assert(static_cast<size_t>(len) < number_buffer.size());
|
||||
|
||||
// erase thousands separator
|
||||
if (thousands_sep != '\0')
|
||||
{
|
||||
const auto end = std::remove(number_buffer.begin(),
|
||||
number_buffer.begin() + len,
|
||||
thousands_sep);
|
||||
std::fill(end, number_buffer.end(), '\0');
|
||||
assert((end - number_buffer.begin()) <= len);
|
||||
len = (end - number_buffer.begin());
|
||||
}
|
||||
|
||||
// convert decimal point to '.'
|
||||
if (decimal_point != '\0' && decimal_point != '.')
|
||||
{
|
||||
for (auto& c : number_buffer)
|
||||
{
|
||||
if (c == decimal_point)
|
||||
{
|
||||
c = '.';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
o.write(number_buffer.data(), static_cast<size_t>(len));
|
||||
|
||||
// determine if need to append ".0"
|
||||
const bool value_is_int_like = std::none_of(number_buffer.begin(),
|
||||
number_buffer.begin() + len + 1,
|
||||
[](char c)
|
||||
{
|
||||
return c == '.' || c == 'e';
|
||||
});
|
||||
|
||||
if (value_is_int_like)
|
||||
{
|
||||
o << ".0";
|
||||
}
|
||||
}
|
||||
|
||||
namespace wpi {
|
||||
|
||||
llvm::raw_ostream& operator<<(llvm::raw_ostream& o, const json& j)
|
||||
{
|
||||
j.dump(o, 0);
|
||||
return o;
|
||||
}
|
||||
|
||||
} // namespace wpi
|
||||
|
||||
std::string json::dump(int indent) const
|
||||
{
|
||||
std::string s;
|
||||
llvm::raw_string_ostream os(s);
|
||||
dump(os, indent);
|
||||
os.flush();
|
||||
return s;
|
||||
}
|
||||
|
||||
void json::dump(llvm::raw_ostream& os, int indent) const
|
||||
{
|
||||
serializer s(os);
|
||||
|
||||
if (indent >= 0)
|
||||
{
|
||||
s.dump(*this, true, static_cast<unsigned int>(indent));
|
||||
}
|
||||
else
|
||||
{
|
||||
s.dump(*this, false, 0);
|
||||
}
|
||||
}
|
||||
120
wpiutil/src/main/native/cpp/support/json_serializer.h
Normal file
120
wpiutil/src/main/native/cpp/support/json_serializer.h
Normal file
@@ -0,0 +1,120 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Modifications Copyright (c) FIRST 2017. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/*
|
||||
__ _____ _____ _____
|
||||
__| | __| | | | JSON for Modern C++
|
||||
| | |__ | | | | | | version 2.1.1
|
||||
|_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
|
||||
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
Copyright (c) 2013-2017 Niels Lohmann <http://nlohmann.me>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#include "support/json.h"
|
||||
|
||||
#include <clocale> // lconv, localeconv
|
||||
#include <locale> // locale
|
||||
|
||||
#include "llvm/raw_ostream.h"
|
||||
|
||||
namespace wpi {
|
||||
|
||||
/*!
|
||||
@brief wrapper around the serialization functions
|
||||
*/
|
||||
class json::serializer
|
||||
{
|
||||
public:
|
||||
serializer(const serializer&) = delete;
|
||||
serializer& operator=(const serializer&) = delete;
|
||||
|
||||
/*!
|
||||
@param[in] s output stream to serialize to
|
||||
@param[in] ichar indentation character to use
|
||||
*/
|
||||
explicit serializer(llvm::raw_ostream& s)
|
||||
: o(s), loc(std::localeconv()),
|
||||
thousands_sep(!loc->thousands_sep ? '\0' : loc->thousands_sep[0]),
|
||||
decimal_point(!loc->decimal_point ? '\0' : loc->decimal_point[0])
|
||||
{}
|
||||
|
||||
/*!
|
||||
@brief internal implementation of the serialization function
|
||||
|
||||
This function is called by the public member function dump and
|
||||
organizes the serialization internally. The indentation level is
|
||||
propagated as additional parameter. In case of arrays and objects, the
|
||||
function is called recursively.
|
||||
|
||||
- strings and object keys are escaped using `escape_string()`
|
||||
- integer numbers are converted implicitly via `operator<<`
|
||||
- floating-point numbers are converted to a string using `"%g"` format
|
||||
|
||||
@param[in] val value to serialize
|
||||
@param[in] pretty_print whether the output shall be pretty-printed
|
||||
@param[in] indent_step the indent level
|
||||
@param[in] current_indent the current indent level (only used internally)
|
||||
*/
|
||||
void dump(const json& val,
|
||||
const bool pretty_print,
|
||||
const unsigned int indent_step,
|
||||
const unsigned int current_indent = 0);
|
||||
|
||||
/*!
|
||||
@brief dump escaped string
|
||||
|
||||
Escape a string by replacing certain special characters by a sequence
|
||||
of an escape character (backslash) and another character and other
|
||||
control characters by a sequence of "\u" followed by a four-digit hex
|
||||
representation. The escaped string is written to output stream @a o.
|
||||
|
||||
@param[in] s the string to escape
|
||||
|
||||
@complexity Linear in the length of string @a s.
|
||||
*/
|
||||
void dump_escaped(llvm::StringRef s) const;
|
||||
|
||||
/*!
|
||||
@brief dump a floating-point number
|
||||
|
||||
Dump a given floating-point number to output stream @a o. Works
|
||||
internally with @a number_buffer.
|
||||
|
||||
@param[in] x floating-point number to dump
|
||||
*/
|
||||
void dump_float(double x);
|
||||
|
||||
private:
|
||||
/// the output of the serializer
|
||||
llvm::raw_ostream& o;
|
||||
|
||||
/// the locale
|
||||
const std::lconv* loc = nullptr;
|
||||
/// the locale's thousand separator character
|
||||
const char thousands_sep = '\0';
|
||||
/// the locale's decimal point character
|
||||
const char decimal_point = '\0';
|
||||
};
|
||||
|
||||
} // namespace wpi
|
||||
120
wpiutil/src/main/native/cpp/support/leb128.cpp
Normal file
120
wpiutil/src/main/native/cpp/support/leb128.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2015-2018 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "support/leb128.h"
|
||||
|
||||
#include "support/raw_istream.h"
|
||||
|
||||
namespace wpi {
|
||||
|
||||
/**
|
||||
* Get size of unsigned LEB128 data
|
||||
* @val: value
|
||||
*
|
||||
* Determine the number of bytes required to encode an unsigned LEB128 datum.
|
||||
* The algorithm is taken from Appendix C of the DWARF 3 spec. For information
|
||||
* on the encodings refer to section "7.6 - Variable Length Data". Return
|
||||
* the number of bytes required.
|
||||
*/
|
||||
uint64_t SizeUleb128(uint64_t val) {
|
||||
size_t count = 0;
|
||||
do {
|
||||
val >>= 7;
|
||||
++count;
|
||||
} while (val != 0);
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write unsigned LEB128 data
|
||||
* @addr: the address where the ULEB128 data is to be stored
|
||||
* @val: value to be stored
|
||||
*
|
||||
* Encode an unsigned LEB128 encoded datum. The algorithm is taken
|
||||
* from Appendix C of the DWARF 3 spec. For information on the
|
||||
* encodings refer to section "7.6 - Variable Length Data". Return
|
||||
* the number of bytes written.
|
||||
*/
|
||||
uint64_t WriteUleb128(llvm::SmallVectorImpl<char>& dest, uint64_t val) {
|
||||
size_t count = 0;
|
||||
|
||||
do {
|
||||
unsigned char byte = val & 0x7f;
|
||||
val >>= 7;
|
||||
|
||||
if (val != 0)
|
||||
byte |= 0x80; // mark this byte to show that more bytes will follow
|
||||
|
||||
dest.push_back(byte);
|
||||
count++;
|
||||
} while (val != 0);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read unsigned LEB128 data
|
||||
* @addr: the address where the ULEB128 data is stored
|
||||
* @ret: address to store the result
|
||||
*
|
||||
* Decode an unsigned LEB128 encoded datum. The algorithm is taken
|
||||
* from Appendix C of the DWARF 3 spec. For information on the
|
||||
* encodings refer to section "7.6 - Variable Length Data". Return
|
||||
* the number of bytes read.
|
||||
*/
|
||||
uint64_t ReadUleb128(const char* addr, uint64_t* ret) {
|
||||
uint32_t result = 0;
|
||||
int shift = 0;
|
||||
size_t count = 0;
|
||||
|
||||
while (1) {
|
||||
unsigned char byte = *reinterpret_cast<const unsigned char*>(addr);
|
||||
addr++;
|
||||
count++;
|
||||
|
||||
result |= (byte & 0x7f) << shift;
|
||||
shift += 7;
|
||||
|
||||
if (!(byte & 0x80)) break;
|
||||
}
|
||||
|
||||
*ret = result;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read unsigned LEB128 data from a stream
|
||||
* @is: the input stream where the ULEB128 data is to be read from
|
||||
* @ret: address to store the result
|
||||
*
|
||||
* Decode an unsigned LEB128 encoded datum. The algorithm is taken
|
||||
* from Appendix C of the DWARF 3 spec. For information on the
|
||||
* encodings refer to section "7.6 - Variable Length Data". Return
|
||||
* false on stream error, true on success.
|
||||
*/
|
||||
bool ReadUleb128(raw_istream& is, uint64_t* ret) {
|
||||
uint32_t result = 0;
|
||||
int shift = 0;
|
||||
|
||||
while (1) {
|
||||
unsigned char byte;
|
||||
is.read(reinterpret_cast<char*>(&byte), 1);
|
||||
if (is.has_error()) return false;
|
||||
|
||||
result |= (byte & 0x7f) << shift;
|
||||
shift += 7;
|
||||
|
||||
if (!(byte & 0x80)) break;
|
||||
}
|
||||
|
||||
*ret = result;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace wpi
|
||||
134
wpiutil/src/main/native/cpp/support/raw_istream.cpp
Normal file
134
wpiutil/src/main/native/cpp/support/raw_istream.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2015-2018 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "support/raw_istream.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "llvm/FileSystem.h"
|
||||
#include "llvm/SmallVector.h"
|
||||
#include "llvm/StringRef.h"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#ifndef STDIN_FILENO
|
||||
#define STDIN_FILENO 0
|
||||
#endif
|
||||
#ifndef STDOUT_FILENO
|
||||
#define STDOUT_FILENO 1
|
||||
#endif
|
||||
#ifndef STDERR_FILENO
|
||||
#define STDERR_FILENO 2
|
||||
#endif
|
||||
#endif
|
||||
|
||||
using namespace wpi;
|
||||
|
||||
llvm::StringRef raw_istream::getline(llvm::SmallVectorImpl<char>& buf,
|
||||
int maxLen) {
|
||||
buf.clear();
|
||||
for (int i = 0; i < maxLen; ++i) {
|
||||
char c;
|
||||
read(c);
|
||||
if (has_error()) return llvm::StringRef{buf.data(), buf.size()};
|
||||
if (c == '\r') continue;
|
||||
buf.push_back(c);
|
||||
if (c == '\n') break;
|
||||
}
|
||||
return llvm::StringRef{buf.data(), buf.size()};
|
||||
}
|
||||
|
||||
raw_mem_istream::raw_mem_istream(llvm::StringRef mem)
|
||||
: raw_mem_istream(mem.data(), mem.size()) {}
|
||||
|
||||
void raw_mem_istream::close() {}
|
||||
|
||||
size_t raw_mem_istream::in_avail() const { return m_left; }
|
||||
|
||||
void raw_mem_istream::read_impl(void* data, size_t len) {
|
||||
if (len > m_left) {
|
||||
error_detected();
|
||||
return;
|
||||
}
|
||||
std::memcpy(data, m_cur, len);
|
||||
m_cur += len;
|
||||
m_left -= len;
|
||||
}
|
||||
|
||||
static int getFD(const llvm::Twine& Filename, std::error_code& EC) {
|
||||
// Handle "-" as stdin. Note that when we do this, we consider ourself
|
||||
// the owner of stdin. This means that we can do things like close the
|
||||
// file descriptor when we're done and set the "binary" flag globally.
|
||||
if (Filename.isSingleStringRef() && Filename.getSingleStringRef() == "-") {
|
||||
EC = std::error_code();
|
||||
return STDIN_FILENO;
|
||||
}
|
||||
|
||||
int FD;
|
||||
|
||||
EC = llvm::sys::fs::openFileForRead(Filename, FD);
|
||||
if (EC) return -1;
|
||||
|
||||
EC = std::error_code();
|
||||
return FD;
|
||||
}
|
||||
|
||||
raw_fd_istream::raw_fd_istream(const llvm::Twine& filename, std::error_code& ec,
|
||||
size_t bufSize)
|
||||
: raw_fd_istream(getFD(filename, ec), true, bufSize) {}
|
||||
|
||||
raw_fd_istream::raw_fd_istream(int fd, bool shouldClose, size_t bufSize)
|
||||
: m_bufSize(bufSize), m_fd(fd), m_shouldClose(shouldClose) {
|
||||
m_cur = m_end = m_buf = static_cast<char*>(std::malloc(bufSize));
|
||||
}
|
||||
|
||||
raw_fd_istream::~raw_fd_istream() {
|
||||
if (m_shouldClose) close();
|
||||
std::free(m_buf);
|
||||
}
|
||||
|
||||
void raw_fd_istream::close() {
|
||||
if (m_fd >= 0) {
|
||||
::close(m_fd);
|
||||
m_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
size_t raw_fd_istream::in_avail() const { return m_end - m_cur; }
|
||||
|
||||
void raw_fd_istream::read_impl(void* data, size_t len) {
|
||||
size_t left = m_end - m_cur;
|
||||
if (left < len) {
|
||||
// not enough data
|
||||
if (m_cur == m_end) {
|
||||
#ifdef _WIN32
|
||||
int count = ::_read(m_fd, m_buf, m_bufSize);
|
||||
#else
|
||||
ssize_t count = ::read(m_fd, m_buf, m_bufSize);
|
||||
#endif
|
||||
if (count <= 0) {
|
||||
error_detected();
|
||||
return;
|
||||
}
|
||||
m_cur = m_buf;
|
||||
m_end = m_buf + count;
|
||||
return read_impl(data, len);
|
||||
}
|
||||
|
||||
std::memcpy(data, m_cur, left);
|
||||
return read_impl(static_cast<char*>(data) + left, len - left);
|
||||
}
|
||||
|
||||
std::memcpy(data, m_cur, len);
|
||||
m_cur += len;
|
||||
}
|
||||
31
wpiutil/src/main/native/cpp/support/raw_socket_istream.cpp
Normal file
31
wpiutil/src/main/native/cpp/support/raw_socket_istream.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2015-2018 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "support/raw_socket_istream.h"
|
||||
|
||||
#include "tcpsockets/NetworkStream.h"
|
||||
|
||||
using namespace wpi;
|
||||
|
||||
void raw_socket_istream::read_impl(void* data, size_t len) {
|
||||
char* cdata = static_cast<char*>(data);
|
||||
size_t pos = 0;
|
||||
|
||||
while (pos < len) {
|
||||
NetworkStream::Error err;
|
||||
size_t count = m_stream.receive(&cdata[pos], len - pos, &err, m_timeout);
|
||||
if (count == 0) {
|
||||
error_detected();
|
||||
return;
|
||||
}
|
||||
pos += count;
|
||||
}
|
||||
}
|
||||
|
||||
void raw_socket_istream::close() { m_stream.close(); }
|
||||
|
||||
size_t raw_socket_istream::in_avail() const { return 0; }
|
||||
39
wpiutil/src/main/native/cpp/support/raw_socket_ostream.cpp
Normal file
39
wpiutil/src/main/native/cpp/support/raw_socket_ostream.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2015-2018 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "support/raw_socket_ostream.h"
|
||||
|
||||
#include "tcpsockets/NetworkStream.h"
|
||||
|
||||
using namespace wpi;
|
||||
|
||||
raw_socket_ostream::~raw_socket_ostream() {
|
||||
flush();
|
||||
if (m_shouldClose) close();
|
||||
}
|
||||
|
||||
void raw_socket_ostream::write_impl(const char* data, size_t len) {
|
||||
size_t pos = 0;
|
||||
|
||||
while (pos < len) {
|
||||
NetworkStream::Error err;
|
||||
size_t count = m_stream.send(&data[pos], len - pos, &err);
|
||||
if (count == 0) {
|
||||
error_detected();
|
||||
return;
|
||||
}
|
||||
pos += count;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t raw_socket_ostream::current_pos() const { return 0; }
|
||||
|
||||
void raw_socket_ostream::close() {
|
||||
if (!m_shouldClose) return;
|
||||
flush();
|
||||
m_stream.close();
|
||||
}
|
||||
301
wpiutil/src/main/native/cpp/support/sha1.cpp
Normal file
301
wpiutil/src/main/native/cpp/support/sha1.cpp
Normal file
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
sha1.cpp - source code of
|
||||
|
||||
============
|
||||
SHA-1 in C++
|
||||
============
|
||||
|
||||
100% Public Domain.
|
||||
|
||||
Original C Code
|
||||
-- Steve Reid <steve@edmweb.com>
|
||||
Small changes to fit into bglibs
|
||||
-- Bruce Guenter <bruce@untroubled.org>
|
||||
Translation to simpler C++ Code
|
||||
-- Volker Grabsch <vog@notjusthosting.com>
|
||||
Safety fixes
|
||||
-- Eugene Hopkinson <slowriot at voxelstorm dot com>
|
||||
*/
|
||||
|
||||
#include "support/sha1.h"
|
||||
|
||||
#include "llvm/SmallVector.h"
|
||||
#include "llvm/StringExtras.h"
|
||||
#include "llvm/raw_ostream.h"
|
||||
#include "support/raw_istream.h"
|
||||
|
||||
using namespace wpi;
|
||||
|
||||
static const size_t BLOCK_INTS =
|
||||
16; /* number of 32bit integers per SHA1 block */
|
||||
static const size_t BLOCK_BYTES = BLOCK_INTS * 4;
|
||||
|
||||
static void reset(uint32_t digest[], size_t& buf_size, uint64_t& transforms) {
|
||||
/* SHA1 initialization constants */
|
||||
digest[0] = 0x67452301;
|
||||
digest[1] = 0xefcdab89;
|
||||
digest[2] = 0x98badcfe;
|
||||
digest[3] = 0x10325476;
|
||||
digest[4] = 0xc3d2e1f0;
|
||||
|
||||
/* Reset counters */
|
||||
buf_size = 0;
|
||||
transforms = 0;
|
||||
}
|
||||
|
||||
static uint32_t rol(const uint32_t value, const size_t bits) {
|
||||
return (value << bits) | (value >> (32 - bits));
|
||||
}
|
||||
|
||||
static uint32_t blk(const uint32_t block[BLOCK_INTS], const size_t i) {
|
||||
return rol(block[(i + 13) & 15] ^ block[(i + 8) & 15] ^ block[(i + 2) & 15] ^
|
||||
block[i],
|
||||
1);
|
||||
}
|
||||
|
||||
/*
|
||||
* (R0+R1), R2, R3, R4 are the different operations used in SHA1
|
||||
*/
|
||||
|
||||
static void R0(const uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t& w,
|
||||
const uint32_t x, const uint32_t y, uint32_t& z,
|
||||
const size_t i) {
|
||||
z += ((w & (x ^ y)) ^ y) + block[i] + 0x5a827999 + rol(v, 5);
|
||||
w = rol(w, 30);
|
||||
}
|
||||
|
||||
static void R1(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t& w,
|
||||
const uint32_t x, const uint32_t y, uint32_t& z,
|
||||
const size_t i) {
|
||||
block[i] = blk(block, i);
|
||||
z += ((w & (x ^ y)) ^ y) + block[i] + 0x5a827999 + rol(v, 5);
|
||||
w = rol(w, 30);
|
||||
}
|
||||
|
||||
static void R2(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t& w,
|
||||
const uint32_t x, const uint32_t y, uint32_t& z,
|
||||
const size_t i) {
|
||||
block[i] = blk(block, i);
|
||||
z += (w ^ x ^ y) + block[i] + 0x6ed9eba1 + rol(v, 5);
|
||||
w = rol(w, 30);
|
||||
}
|
||||
|
||||
static void R3(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t& w,
|
||||
const uint32_t x, const uint32_t y, uint32_t& z,
|
||||
const size_t i) {
|
||||
block[i] = blk(block, i);
|
||||
z += (((w | x) & y) | (w & x)) + block[i] + 0x8f1bbcdc + rol(v, 5);
|
||||
w = rol(w, 30);
|
||||
}
|
||||
|
||||
static void R4(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t& w,
|
||||
const uint32_t x, const uint32_t y, uint32_t& z,
|
||||
const size_t i) {
|
||||
block[i] = blk(block, i);
|
||||
z += (w ^ x ^ y) + block[i] + 0xca62c1d6 + rol(v, 5);
|
||||
w = rol(w, 30);
|
||||
}
|
||||
|
||||
/*
|
||||
* Hash a single 512-bit block. This is the core of the algorithm.
|
||||
*/
|
||||
|
||||
static void transform(uint32_t digest[], uint32_t block[BLOCK_INTS],
|
||||
uint64_t& transforms) {
|
||||
/* Copy digest[] to working vars */
|
||||
uint32_t a = digest[0];
|
||||
uint32_t b = digest[1];
|
||||
uint32_t c = digest[2];
|
||||
uint32_t d = digest[3];
|
||||
uint32_t e = digest[4];
|
||||
|
||||
/* 4 rounds of 20 operations each. Loop unrolled. */
|
||||
R0(block, a, b, c, d, e, 0);
|
||||
R0(block, e, a, b, c, d, 1);
|
||||
R0(block, d, e, a, b, c, 2);
|
||||
R0(block, c, d, e, a, b, 3);
|
||||
R0(block, b, c, d, e, a, 4);
|
||||
R0(block, a, b, c, d, e, 5);
|
||||
R0(block, e, a, b, c, d, 6);
|
||||
R0(block, d, e, a, b, c, 7);
|
||||
R0(block, c, d, e, a, b, 8);
|
||||
R0(block, b, c, d, e, a, 9);
|
||||
R0(block, a, b, c, d, e, 10);
|
||||
R0(block, e, a, b, c, d, 11);
|
||||
R0(block, d, e, a, b, c, 12);
|
||||
R0(block, c, d, e, a, b, 13);
|
||||
R0(block, b, c, d, e, a, 14);
|
||||
R0(block, a, b, c, d, e, 15);
|
||||
R1(block, e, a, b, c, d, 0);
|
||||
R1(block, d, e, a, b, c, 1);
|
||||
R1(block, c, d, e, a, b, 2);
|
||||
R1(block, b, c, d, e, a, 3);
|
||||
R2(block, a, b, c, d, e, 4);
|
||||
R2(block, e, a, b, c, d, 5);
|
||||
R2(block, d, e, a, b, c, 6);
|
||||
R2(block, c, d, e, a, b, 7);
|
||||
R2(block, b, c, d, e, a, 8);
|
||||
R2(block, a, b, c, d, e, 9);
|
||||
R2(block, e, a, b, c, d, 10);
|
||||
R2(block, d, e, a, b, c, 11);
|
||||
R2(block, c, d, e, a, b, 12);
|
||||
R2(block, b, c, d, e, a, 13);
|
||||
R2(block, a, b, c, d, e, 14);
|
||||
R2(block, e, a, b, c, d, 15);
|
||||
R2(block, d, e, a, b, c, 0);
|
||||
R2(block, c, d, e, a, b, 1);
|
||||
R2(block, b, c, d, e, a, 2);
|
||||
R2(block, a, b, c, d, e, 3);
|
||||
R2(block, e, a, b, c, d, 4);
|
||||
R2(block, d, e, a, b, c, 5);
|
||||
R2(block, c, d, e, a, b, 6);
|
||||
R2(block, b, c, d, e, a, 7);
|
||||
R3(block, a, b, c, d, e, 8);
|
||||
R3(block, e, a, b, c, d, 9);
|
||||
R3(block, d, e, a, b, c, 10);
|
||||
R3(block, c, d, e, a, b, 11);
|
||||
R3(block, b, c, d, e, a, 12);
|
||||
R3(block, a, b, c, d, e, 13);
|
||||
R3(block, e, a, b, c, d, 14);
|
||||
R3(block, d, e, a, b, c, 15);
|
||||
R3(block, c, d, e, a, b, 0);
|
||||
R3(block, b, c, d, e, a, 1);
|
||||
R3(block, a, b, c, d, e, 2);
|
||||
R3(block, e, a, b, c, d, 3);
|
||||
R3(block, d, e, a, b, c, 4);
|
||||
R3(block, c, d, e, a, b, 5);
|
||||
R3(block, b, c, d, e, a, 6);
|
||||
R3(block, a, b, c, d, e, 7);
|
||||
R3(block, e, a, b, c, d, 8);
|
||||
R3(block, d, e, a, b, c, 9);
|
||||
R3(block, c, d, e, a, b, 10);
|
||||
R3(block, b, c, d, e, a, 11);
|
||||
R4(block, a, b, c, d, e, 12);
|
||||
R4(block, e, a, b, c, d, 13);
|
||||
R4(block, d, e, a, b, c, 14);
|
||||
R4(block, c, d, e, a, b, 15);
|
||||
R4(block, b, c, d, e, a, 0);
|
||||
R4(block, a, b, c, d, e, 1);
|
||||
R4(block, e, a, b, c, d, 2);
|
||||
R4(block, d, e, a, b, c, 3);
|
||||
R4(block, c, d, e, a, b, 4);
|
||||
R4(block, b, c, d, e, a, 5);
|
||||
R4(block, a, b, c, d, e, 6);
|
||||
R4(block, e, a, b, c, d, 7);
|
||||
R4(block, d, e, a, b, c, 8);
|
||||
R4(block, c, d, e, a, b, 9);
|
||||
R4(block, b, c, d, e, a, 10);
|
||||
R4(block, a, b, c, d, e, 11);
|
||||
R4(block, e, a, b, c, d, 12);
|
||||
R4(block, d, e, a, b, c, 13);
|
||||
R4(block, c, d, e, a, b, 14);
|
||||
R4(block, b, c, d, e, a, 15);
|
||||
|
||||
/* Add the working vars back into digest[] */
|
||||
digest[0] += a;
|
||||
digest[1] += b;
|
||||
digest[2] += c;
|
||||
digest[3] += d;
|
||||
digest[4] += e;
|
||||
|
||||
/* Count the number of transformations */
|
||||
transforms++;
|
||||
}
|
||||
|
||||
static void buffer_to_block(const unsigned char* buffer,
|
||||
uint32_t block[BLOCK_INTS]) {
|
||||
/* Convert the std::string (byte buffer) to a uint32_t array (MSB) */
|
||||
for (size_t i = 0; i < BLOCK_INTS; i++) {
|
||||
block[i] = (buffer[4 * i + 3] & 0xff) | (buffer[4 * i + 2] & 0xff) << 8 |
|
||||
(buffer[4 * i + 1] & 0xff) << 16 |
|
||||
(buffer[4 * i + 0] & 0xff) << 24;
|
||||
}
|
||||
}
|
||||
|
||||
SHA1::SHA1() { reset(digest, buf_size, transforms); }
|
||||
|
||||
void SHA1::Update(llvm::StringRef s) {
|
||||
raw_mem_istream is(s);
|
||||
Update(is);
|
||||
}
|
||||
|
||||
void SHA1::Update(raw_istream& is) {
|
||||
while (true) {
|
||||
buf_size += is.readsome(&buffer[buf_size], BLOCK_BYTES - buf_size);
|
||||
if (buf_size != BLOCK_BYTES) {
|
||||
return;
|
||||
}
|
||||
uint32_t block[BLOCK_INTS];
|
||||
buffer_to_block(buffer, block);
|
||||
transform(digest, block, transforms);
|
||||
buf_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Add padding and return the message digest.
|
||||
*/
|
||||
|
||||
static void finalize(uint32_t digest[], unsigned char* buffer, size_t& buf_size,
|
||||
uint64_t& transforms, llvm::raw_ostream& os) {
|
||||
/* Total number of hashed bits */
|
||||
uint64_t total_bits = (transforms * BLOCK_BYTES + buf_size) * 8;
|
||||
|
||||
/* Padding */
|
||||
buffer[buf_size++] = 0x80;
|
||||
for (size_t i = buf_size; i < BLOCK_BYTES; ++i) {
|
||||
buffer[i] = 0x00;
|
||||
}
|
||||
|
||||
uint32_t block[BLOCK_INTS];
|
||||
buffer_to_block(buffer, block);
|
||||
|
||||
if (buf_size > BLOCK_BYTES - 8) {
|
||||
transform(digest, block, transforms);
|
||||
for (size_t i = 0; i < BLOCK_INTS - 2; i++) {
|
||||
block[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Append total_bits, split this uint64_t into two uint32_t */
|
||||
block[BLOCK_INTS - 1] = total_bits;
|
||||
block[BLOCK_INTS - 2] = (total_bits >> 32);
|
||||
transform(digest, block, transforms);
|
||||
|
||||
/* Hex string */
|
||||
static const char* const LUT = "0123456789abcdef";
|
||||
for (size_t i = 0; i < 5; i++) {
|
||||
uint32_t v = digest[i];
|
||||
os << LUT[(v >> 28) & 0xf] << LUT[(v >> 24) & 0xf] << LUT[(v >> 20) & 0xf]
|
||||
<< LUT[(v >> 16) & 0xf] << LUT[(v >> 12) & 0xf] << LUT[(v >> 8) & 0xf]
|
||||
<< LUT[(v >> 4) & 0xf] << LUT[(v >> 0) & 0xf];
|
||||
}
|
||||
|
||||
/* Reset for next run */
|
||||
reset(digest, buf_size, transforms);
|
||||
}
|
||||
|
||||
std::string SHA1::Final() {
|
||||
std::string out;
|
||||
llvm::raw_string_ostream os(out);
|
||||
|
||||
finalize(digest, buffer, buf_size, transforms, os);
|
||||
|
||||
return os.str();
|
||||
}
|
||||
|
||||
llvm::StringRef SHA1::Final(llvm::SmallVectorImpl<char>& buf) {
|
||||
llvm::raw_svector_ostream os(buf);
|
||||
|
||||
finalize(digest, buffer, buf_size, transforms, os);
|
||||
|
||||
return os.str();
|
||||
}
|
||||
|
||||
std::string SHA1::FromFile(llvm::StringRef filename) {
|
||||
std::error_code ec;
|
||||
raw_fd_istream stream(filename, ec);
|
||||
SHA1 checksum;
|
||||
checksum.Update(stream);
|
||||
return checksum.Final();
|
||||
}
|
||||
108
wpiutil/src/main/native/cpp/support/timestamp.cpp
Normal file
108
wpiutil/src/main/native/cpp/support/timestamp.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2015-2018 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "support/timestamp.h"
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <exception>
|
||||
#else
|
||||
#include <chrono>
|
||||
#endif
|
||||
|
||||
// offset in microseconds
|
||||
static uint64_t zerotime() {
|
||||
#ifdef _WIN32
|
||||
FILETIME ft;
|
||||
uint64_t tmpres = 0;
|
||||
// 100-nanosecond intervals since January 1, 1601 (UTC)
|
||||
// which means 0.1 us
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
tmpres |= ft.dwHighDateTime;
|
||||
tmpres <<= 32;
|
||||
tmpres |= ft.dwLowDateTime;
|
||||
tmpres /= 10u; // convert to us
|
||||
// January 1st, 1970 - January 1st, 1601 UTC ~ 369 years
|
||||
// or 11644473600000000 us
|
||||
static const uint64_t deltaepoch = 11644473600000000ull;
|
||||
tmpres -= deltaepoch;
|
||||
return tmpres;
|
||||
#else
|
||||
// 1-us intervals
|
||||
return std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::high_resolution_clock::now().time_since_epoch())
|
||||
.count();
|
||||
#endif
|
||||
}
|
||||
|
||||
static uint64_t timestamp() {
|
||||
#ifdef _WIN32
|
||||
LARGE_INTEGER li;
|
||||
QueryPerformanceCounter(&li);
|
||||
// there is an imprecision with the initial value,
|
||||
// but what matters is that timestamps are monotonic and consistent
|
||||
return static_cast<uint64_t>(li.QuadPart);
|
||||
#else
|
||||
// 1-us intervals
|
||||
return std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch())
|
||||
.count();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static uint64_t update_frequency() {
|
||||
LARGE_INTEGER li;
|
||||
if (!QueryPerformanceFrequency(&li) || !li.QuadPart) {
|
||||
// log something
|
||||
std::terminate();
|
||||
}
|
||||
return static_cast<uint64_t>(li.QuadPart);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const uint64_t zerotime_val = zerotime();
|
||||
static const uint64_t offset_val = timestamp();
|
||||
#ifdef _WIN32
|
||||
static const uint64_t frequency_val = update_frequency();
|
||||
#endif
|
||||
|
||||
uint64_t wpi::NowDefault() {
|
||||
#ifdef _WIN32
|
||||
assert(offset_val > 0u);
|
||||
assert(frequency_val > 0u);
|
||||
uint64_t delta = timestamp() - offset_val;
|
||||
// because the frequency is in update per seconds, we have to multiply the
|
||||
// delta by 1,000,000
|
||||
uint64_t delta_in_us = delta * 1000000ull / frequency_val;
|
||||
return delta_in_us + zerotime_val;
|
||||
#else
|
||||
return zerotime_val + timestamp() - offset_val;
|
||||
#endif
|
||||
}
|
||||
|
||||
static std::atomic<uint64_t (*)()> now_impl{wpi::NowDefault};
|
||||
|
||||
void wpi::SetNowImpl(uint64_t (*func)(void)) {
|
||||
now_impl = func ? func : NowDefault;
|
||||
}
|
||||
|
||||
uint64_t wpi::Now() { return (now_impl.load())(); }
|
||||
|
||||
extern "C" {
|
||||
|
||||
uint64_t WPI_NowDefault(void) { return wpi::NowDefault(); }
|
||||
|
||||
void WPI_SetNowImpl(uint64_t (*func)(void)) { wpi::SetNowImpl(func); }
|
||||
|
||||
uint64_t WPI_Now(void) { return wpi::Now(); }
|
||||
|
||||
} // extern "C"
|
||||
40
wpiutil/src/main/native/cpp/tcpsockets/SocketError.cpp
Normal file
40
wpiutil/src/main/native/cpp/tcpsockets/SocketError.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2015-2018 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "tcpsockets/SocketError.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#endif
|
||||
|
||||
namespace wpi {
|
||||
|
||||
int SocketErrno() {
|
||||
#ifdef _WIN32
|
||||
return WSAGetLastError();
|
||||
#else
|
||||
return errno;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string SocketStrerror(int code) {
|
||||
#ifdef _WIN32
|
||||
LPSTR errstr = nullptr;
|
||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 0,
|
||||
code, 0, (LPSTR)&errstr, 0, 0);
|
||||
std::string rv(errstr);
|
||||
LocalFree(errstr);
|
||||
return rv;
|
||||
#else
|
||||
return std::strerror(code);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace wpi
|
||||
205
wpiutil/src/main/native/cpp/tcpsockets/TCPAcceptor.cpp
Normal file
205
wpiutil/src/main/native/cpp/tcpsockets/TCPAcceptor.cpp
Normal file
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
TCPAcceptor.cpp
|
||||
|
||||
TCPAcceptor class definition. TCPAcceptor provides methods to passively
|
||||
establish TCP/IP connections with clients.
|
||||
|
||||
------------------------------------------
|
||||
|
||||
Copyright (c) 2013 [Vic Hargrave - http://vichargrave.com]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include "tcpsockets/TCPAcceptor.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <WinSock2.h>
|
||||
#include <Ws2tcpip.h>
|
||||
#pragma comment(lib, "Ws2_32.lib")
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#include <fcntl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "llvm/SmallString.h"
|
||||
#include "support/Logger.h"
|
||||
#include "tcpsockets/SocketError.h"
|
||||
|
||||
using namespace wpi;
|
||||
|
||||
TCPAcceptor::TCPAcceptor(int port, const char* address, Logger& logger)
|
||||
: m_lsd(0),
|
||||
m_port(port),
|
||||
m_address(address),
|
||||
m_listening(false),
|
||||
m_logger(logger) {
|
||||
m_shutdown = false;
|
||||
#ifdef _WIN32
|
||||
WSAData wsaData;
|
||||
WORD wVersionRequested = MAKEWORD(2, 2);
|
||||
WSAStartup(wVersionRequested, &wsaData);
|
||||
#endif
|
||||
}
|
||||
|
||||
TCPAcceptor::~TCPAcceptor() {
|
||||
if (m_lsd > 0) {
|
||||
shutdown();
|
||||
#ifdef _WIN32
|
||||
closesocket(m_lsd);
|
||||
#else
|
||||
close(m_lsd);
|
||||
#endif
|
||||
}
|
||||
#ifdef _WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
}
|
||||
|
||||
int TCPAcceptor::start() {
|
||||
if (m_listening) return 0;
|
||||
|
||||
m_lsd = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (m_lsd < 0) {
|
||||
WPI_ERROR(m_logger, "could not create socket");
|
||||
return -1;
|
||||
}
|
||||
struct sockaddr_in address;
|
||||
|
||||
std::memset(&address, 0, sizeof(address));
|
||||
address.sin_family = PF_INET;
|
||||
if (m_address.size() > 0) {
|
||||
#ifdef _WIN32
|
||||
llvm::SmallString<128> addr_copy(m_address);
|
||||
addr_copy.push_back('\0');
|
||||
int res = InetPton(PF_INET, addr_copy.data(), &(address.sin_addr));
|
||||
#else
|
||||
int res = inet_pton(PF_INET, m_address.c_str(), &(address.sin_addr));
|
||||
#endif
|
||||
if (res != 1) {
|
||||
WPI_ERROR(m_logger, "could not resolve " << m_address << " address");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
address.sin_addr.s_addr = INADDR_ANY;
|
||||
}
|
||||
address.sin_port = htons(m_port);
|
||||
|
||||
#ifdef _WIN32
|
||||
int optval = 1;
|
||||
setsockopt(m_lsd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
|
||||
reinterpret_cast<char*>(&optval), sizeof optval);
|
||||
#else
|
||||
int optval = 1;
|
||||
setsockopt(m_lsd, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&optval),
|
||||
sizeof optval);
|
||||
#endif
|
||||
|
||||
int result = bind(m_lsd, reinterpret_cast<struct sockaddr*>(&address),
|
||||
sizeof(address));
|
||||
if (result != 0) {
|
||||
WPI_ERROR(m_logger,
|
||||
"bind() to port " << m_port << " failed: " << SocketStrerror());
|
||||
return result;
|
||||
}
|
||||
|
||||
result = listen(m_lsd, 5);
|
||||
if (result != 0) {
|
||||
WPI_ERROR(m_logger,
|
||||
"listen() on port " << m_port << " failed: " << SocketStrerror());
|
||||
return result;
|
||||
}
|
||||
m_listening = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
void TCPAcceptor::shutdown() {
|
||||
m_shutdown = true;
|
||||
#ifdef _WIN32
|
||||
::shutdown(m_lsd, SD_BOTH);
|
||||
|
||||
// this is ugly, but the easiest way to do this
|
||||
// force wakeup of accept() with a non-blocking connect to ourselves
|
||||
struct sockaddr_in address;
|
||||
|
||||
std::memset(&address, 0, sizeof(address));
|
||||
address.sin_family = PF_INET;
|
||||
llvm::SmallString<128> addr_copy;
|
||||
if (m_address.size() > 0)
|
||||
addr_copy = m_address;
|
||||
else
|
||||
addr_copy = "127.0.0.1";
|
||||
addr_copy.push_back('\0');
|
||||
int size = sizeof(address);
|
||||
if (WSAStringToAddress(addr_copy.data(), PF_INET, nullptr,
|
||||
(struct sockaddr*)&address, &size) != 0)
|
||||
return;
|
||||
address.sin_port = htons(m_port);
|
||||
|
||||
fd_set sdset;
|
||||
struct timeval tv;
|
||||
int result = -1, valopt, sd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sd < 0) return;
|
||||
|
||||
// Set socket to non-blocking
|
||||
u_long mode = 1;
|
||||
ioctlsocket(sd, FIONBIO, &mode);
|
||||
|
||||
// Try to connect
|
||||
::connect(sd, (struct sockaddr*)&address, sizeof(address));
|
||||
|
||||
// Close
|
||||
::closesocket(sd);
|
||||
|
||||
#else
|
||||
::shutdown(m_lsd, SHUT_RDWR);
|
||||
int nullfd = ::open("/dev/null", O_RDONLY);
|
||||
if (nullfd >= 0) {
|
||||
::dup2(nullfd, m_lsd);
|
||||
::close(nullfd);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
std::unique_ptr<NetworkStream> TCPAcceptor::accept() {
|
||||
if (!m_listening || m_shutdown) return nullptr;
|
||||
|
||||
struct sockaddr_in address;
|
||||
#ifdef _WIN32
|
||||
int len = sizeof(address);
|
||||
#else
|
||||
socklen_t len = sizeof(address);
|
||||
#endif
|
||||
std::memset(&address, 0, sizeof(address));
|
||||
int sd = ::accept(m_lsd, (struct sockaddr*)&address, &len);
|
||||
if (sd < 0) {
|
||||
if (!m_shutdown)
|
||||
WPI_ERROR(m_logger, "accept() on port "
|
||||
<< m_port << " failed: " << SocketStrerror());
|
||||
return nullptr;
|
||||
}
|
||||
if (m_shutdown) {
|
||||
#ifdef _WIN32
|
||||
closesocket(sd);
|
||||
#else
|
||||
close(sd);
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
return std::unique_ptr<NetworkStream>(new TCPStream(sd, &address));
|
||||
}
|
||||
218
wpiutil/src/main/native/cpp/tcpsockets/TCPConnector.cpp
Normal file
218
wpiutil/src/main/native/cpp/tcpsockets/TCPConnector.cpp
Normal file
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
TCPConnector.h
|
||||
|
||||
TCPConnector class definition. TCPConnector provides methods to actively
|
||||
establish TCP/IP connections with a server.
|
||||
|
||||
------------------------------------------
|
||||
|
||||
Copyright (c) 2013 [Vic Hargrave - http://vichargrave.com]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License
|
||||
*/
|
||||
|
||||
#include "tcpsockets/TCPConnector.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <WS2tcpip.h>
|
||||
#include <WinSock2.h>
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/select.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "llvm/SmallString.h"
|
||||
#include "support/Logger.h"
|
||||
#include "tcpsockets/SocketError.h"
|
||||
#include "tcpsockets/TCPStream.h"
|
||||
|
||||
using namespace wpi;
|
||||
|
||||
static int ResolveHostName(const char* hostname, struct in_addr* addr) {
|
||||
struct addrinfo hints;
|
||||
struct addrinfo* res;
|
||||
|
||||
hints.ai_flags = 0;
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = 0;
|
||||
hints.ai_addrlen = 0;
|
||||
hints.ai_addr = nullptr;
|
||||
hints.ai_canonname = nullptr;
|
||||
hints.ai_next = nullptr;
|
||||
int result = getaddrinfo(hostname, nullptr, &hints, &res);
|
||||
if (result == 0) {
|
||||
std::memcpy(
|
||||
addr, &(reinterpret_cast<struct sockaddr_in*>(res->ai_addr)->sin_addr),
|
||||
sizeof(struct in_addr));
|
||||
freeaddrinfo(res);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::unique_ptr<NetworkStream> TCPConnector::connect(const char* server,
|
||||
int port, Logger& logger,
|
||||
int timeout) {
|
||||
#ifdef _WIN32
|
||||
struct WSAHelper {
|
||||
WSAHelper() {
|
||||
WSAData wsaData;
|
||||
WORD wVersionRequested = MAKEWORD(2, 2);
|
||||
WSAStartup(wVersionRequested, &wsaData);
|
||||
}
|
||||
~WSAHelper() { WSACleanup(); }
|
||||
};
|
||||
static WSAHelper helper;
|
||||
#endif
|
||||
struct sockaddr_in address;
|
||||
|
||||
std::memset(&address, 0, sizeof(address));
|
||||
address.sin_family = AF_INET;
|
||||
if (ResolveHostName(server, &(address.sin_addr)) != 0) {
|
||||
#ifdef _WIN32
|
||||
llvm::SmallString<128> addr_copy(server);
|
||||
addr_copy.push_back('\0');
|
||||
int res = InetPton(PF_INET, addr_copy.data(), &(address.sin_addr));
|
||||
#else
|
||||
int res = inet_pton(PF_INET, server, &(address.sin_addr));
|
||||
#endif
|
||||
if (res != 1) {
|
||||
WPI_ERROR(logger, "could not resolve " << server << " address");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
address.sin_port = htons(port);
|
||||
|
||||
if (timeout == 0) {
|
||||
int sd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sd < 0) {
|
||||
WPI_ERROR(logger, "could not create socket");
|
||||
return nullptr;
|
||||
}
|
||||
if (::connect(sd, (struct sockaddr*)&address, sizeof(address)) != 0) {
|
||||
WPI_ERROR(logger, "connect() to " << server << " port " << port
|
||||
<< " failed: " << SocketStrerror());
|
||||
#ifdef _WIN32
|
||||
closesocket(sd);
|
||||
#else
|
||||
::close(sd);
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
return std::unique_ptr<NetworkStream>(new TCPStream(sd, &address));
|
||||
}
|
||||
|
||||
fd_set sdset;
|
||||
struct timeval tv;
|
||||
socklen_t len;
|
||||
int result = -1, valopt, sd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sd < 0) {
|
||||
WPI_ERROR(logger, "could not create socket");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Set socket to non-blocking
|
||||
#ifdef _WIN32
|
||||
u_long mode = 1;
|
||||
if (ioctlsocket(sd, FIONBIO, &mode) == SOCKET_ERROR)
|
||||
WPI_WARNING(logger,
|
||||
"could not set socket to non-blocking: " << SocketStrerror());
|
||||
#else
|
||||
int arg;
|
||||
arg = fcntl(sd, F_GETFL, nullptr);
|
||||
if (arg < 0) {
|
||||
WPI_WARNING(logger,
|
||||
"could not set socket to non-blocking: " << SocketStrerror());
|
||||
} else {
|
||||
arg |= O_NONBLOCK;
|
||||
if (fcntl(sd, F_SETFL, arg) < 0)
|
||||
WPI_WARNING(logger,
|
||||
"could not set socket to non-blocking: " << SocketStrerror());
|
||||
}
|
||||
#endif
|
||||
|
||||
// Connect with time limit
|
||||
if ((result = ::connect(sd, (struct sockaddr*)&address, sizeof(address))) <
|
||||
0) {
|
||||
int my_errno = SocketErrno();
|
||||
#ifdef _WIN32
|
||||
if (my_errno == WSAEWOULDBLOCK || my_errno == WSAEINPROGRESS) {
|
||||
#else
|
||||
if (my_errno == EWOULDBLOCK || my_errno == EINPROGRESS) {
|
||||
#endif
|
||||
tv.tv_sec = timeout;
|
||||
tv.tv_usec = 0;
|
||||
FD_ZERO(&sdset);
|
||||
FD_SET(sd, &sdset);
|
||||
if (select(sd + 1, nullptr, &sdset, nullptr, &tv) > 0) {
|
||||
len = sizeof(int);
|
||||
getsockopt(sd, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&valopt),
|
||||
&len);
|
||||
if (valopt) {
|
||||
WPI_ERROR(logger, "select() to " << server << " port " << port
|
||||
<< " error " << valopt << " - "
|
||||
<< SocketStrerror(valopt));
|
||||
}
|
||||
// connection established
|
||||
else
|
||||
result = 0;
|
||||
} else {
|
||||
WPI_INFO(logger,
|
||||
"connect() to " << server << " port " << port << " timed out");
|
||||
}
|
||||
} else {
|
||||
WPI_ERROR(logger, "connect() to " << server << " port " << port
|
||||
<< " error " << SocketErrno() << " - "
|
||||
<< SocketStrerror());
|
||||
}
|
||||
}
|
||||
|
||||
// Return socket to blocking mode
|
||||
#ifdef _WIN32
|
||||
mode = 0;
|
||||
if (ioctlsocket(sd, FIONBIO, &mode) == SOCKET_ERROR)
|
||||
WPI_WARNING(logger,
|
||||
"could not set socket to blocking: " << SocketStrerror());
|
||||
#else
|
||||
arg = fcntl(sd, F_GETFL, nullptr);
|
||||
if (arg < 0) {
|
||||
WPI_WARNING(logger,
|
||||
"could not set socket to blocking: " << SocketStrerror());
|
||||
} else {
|
||||
arg &= (~O_NONBLOCK);
|
||||
if (fcntl(sd, F_SETFL, arg) < 0)
|
||||
WPI_WARNING(logger,
|
||||
"could not set socket to blocking: " << SocketStrerror());
|
||||
}
|
||||
#endif
|
||||
|
||||
// Create stream object if connected, close if not.
|
||||
if (result == -1) {
|
||||
#ifdef _WIN32
|
||||
closesocket(sd);
|
||||
#else
|
||||
::close(sd);
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
return std::unique_ptr<NetworkStream>(new TCPStream(sd, &address));
|
||||
}
|
||||
129
wpiutil/src/main/native/cpp/tcpsockets/TCPConnector_parallel.cpp
Normal file
129
wpiutil/src/main/native/cpp/tcpsockets/TCPConnector_parallel.cpp
Normal file
@@ -0,0 +1,129 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2017-2018 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "tcpsockets/TCPConnector.h" // NOLINT(build/include_order)
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
|
||||
#include "llvm/SmallSet.h"
|
||||
#include "support/condition_variable.h"
|
||||
#include "support/mutex.h"
|
||||
|
||||
using namespace wpi;
|
||||
|
||||
// MSVC < 1900 doesn't have support for thread_local
|
||||
#if !defined(_MSC_VER) || _MSC_VER >= 1900
|
||||
// clang check for availability of thread_local
|
||||
#if !defined(__has_feature) || __has_feature(cxx_thread_local)
|
||||
#define HAVE_THREAD_LOCAL
|
||||
#endif
|
||||
#endif
|
||||
|
||||
std::unique_ptr<NetworkStream> TCPConnector::connect_parallel(
|
||||
llvm::ArrayRef<std::pair<const char*, int>> servers, Logger& logger,
|
||||
int timeout) {
|
||||
if (servers.empty()) return nullptr;
|
||||
|
||||
// structure to make sure we don't start duplicate workers
|
||||
struct GlobalState {
|
||||
wpi::mutex mtx;
|
||||
#ifdef HAVE_THREAD_LOCAL
|
||||
llvm::SmallSet<std::pair<std::string, int>, 16> active;
|
||||
#else
|
||||
llvm::SmallSet<std::tuple<std::thread::id, std::string, int>, 16> active;
|
||||
#endif
|
||||
};
|
||||
#ifdef HAVE_THREAD_LOCAL
|
||||
thread_local auto global = std::make_shared<GlobalState>();
|
||||
#else
|
||||
static auto global = std::make_shared<GlobalState>();
|
||||
auto this_id = std::this_thread::get_id();
|
||||
#endif
|
||||
auto local = global; // copy to an automatic variable for lambda capture
|
||||
|
||||
// structure shared between threads and this function
|
||||
struct Result {
|
||||
wpi::mutex mtx;
|
||||
wpi::condition_variable cv;
|
||||
std::unique_ptr<NetworkStream> stream;
|
||||
std::atomic<unsigned int> count{0};
|
||||
std::atomic<bool> done{false};
|
||||
};
|
||||
auto result = std::make_shared<Result>();
|
||||
|
||||
// start worker threads; this is I/O bound so we don't limit to # of procs
|
||||
Logger* plogger = &logger;
|
||||
unsigned int num_workers = 0;
|
||||
for (const auto& server : servers) {
|
||||
std::pair<std::string, int> server_copy{std::string{server.first},
|
||||
server.second};
|
||||
#ifdef HAVE_THREAD_LOCAL
|
||||
const auto& active_tracker = server_copy;
|
||||
#else
|
||||
std::tuple<std::thread::id, std::string, int> active_tracker{
|
||||
this_id, server_copy.first, server_copy.second};
|
||||
#endif
|
||||
|
||||
// don't start a new worker if we had a previously still-active connection
|
||||
// attempt to the same server
|
||||
{
|
||||
std::lock_guard<wpi::mutex> lock(local->mtx);
|
||||
if (local->active.count(active_tracker) > 0) continue; // already in set
|
||||
}
|
||||
|
||||
++num_workers;
|
||||
|
||||
// start the worker
|
||||
std::thread([=]() {
|
||||
if (!result->done) {
|
||||
// add to global state
|
||||
{
|
||||
std::lock_guard<wpi::mutex> lock(local->mtx);
|
||||
local->active.insert(active_tracker);
|
||||
}
|
||||
|
||||
// try to connect
|
||||
auto stream = connect(server_copy.first.c_str(), server_copy.second,
|
||||
*plogger, timeout);
|
||||
|
||||
// remove from global state
|
||||
{
|
||||
std::lock_guard<wpi::mutex> lock(local->mtx);
|
||||
local->active.erase(active_tracker);
|
||||
}
|
||||
|
||||
// successful connection
|
||||
if (stream) {
|
||||
std::lock_guard<wpi::mutex> lock(result->mtx);
|
||||
if (!result->done.exchange(true)) result->stream = std::move(stream);
|
||||
}
|
||||
}
|
||||
++result->count;
|
||||
result->cv.notify_all();
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
// wait for a result, timeout, or all finished
|
||||
std::unique_lock<wpi::mutex> lock(result->mtx);
|
||||
if (timeout == 0) {
|
||||
result->cv.wait(
|
||||
lock, [&] { return result->stream || result->count >= num_workers; });
|
||||
} else {
|
||||
auto timeout_time =
|
||||
std::chrono::steady_clock::now() + std::chrono::seconds(timeout);
|
||||
result->cv.wait_until(lock, timeout_time, [&] {
|
||||
return result->stream || result->count >= num_workers;
|
||||
});
|
||||
}
|
||||
|
||||
// no need to wait for remaining worker threads; shared_ptr will clean up
|
||||
return std::move(result->stream);
|
||||
}
|
||||
207
wpiutil/src/main/native/cpp/tcpsockets/TCPStream.cpp
Normal file
207
wpiutil/src/main/native/cpp/tcpsockets/TCPStream.cpp
Normal file
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
TCPStream.h
|
||||
|
||||
TCPStream class definition. TCPStream provides methods to trasnfer
|
||||
data between peers over a TCP/IP connection.
|
||||
|
||||
------------------------------------------
|
||||
|
||||
Copyright (c) 2013 [Vic Hargrave - http://vichargrave.com]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include "tcpsockets/TCPStream.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <WinSock2.h>
|
||||
#include <Ws2tcpip.h>
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
using namespace wpi;
|
||||
|
||||
TCPStream::TCPStream(int sd, sockaddr_in* address)
|
||||
: m_sd(sd), m_blocking(true) {
|
||||
char ip[50];
|
||||
#ifdef _WIN32
|
||||
InetNtop(PF_INET, &(address->sin_addr.s_addr), ip, sizeof(ip) - 1);
|
||||
#else
|
||||
inet_ntop(PF_INET, reinterpret_cast<in_addr*>(&(address->sin_addr.s_addr)),
|
||||
ip, sizeof(ip) - 1);
|
||||
#ifdef SO_NOSIGPIPE
|
||||
// disable SIGPIPE on Mac OS X
|
||||
int set = 1;
|
||||
setsockopt(m_sd, SOL_SOCKET, SO_NOSIGPIPE, reinterpret_cast<char*>(&set),
|
||||
sizeof set);
|
||||
#endif
|
||||
#endif
|
||||
m_peerIP = ip;
|
||||
m_peerPort = ntohs(address->sin_port);
|
||||
}
|
||||
|
||||
TCPStream::~TCPStream() { close(); }
|
||||
|
||||
size_t TCPStream::send(const char* buffer, size_t len, Error* err) {
|
||||
if (m_sd < 0) {
|
||||
*err = kConnectionClosed;
|
||||
return 0;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
WSABUF wsaBuf;
|
||||
wsaBuf.buf = const_cast<char*>(buffer);
|
||||
wsaBuf.len = (ULONG)len;
|
||||
DWORD rv;
|
||||
bool result = true;
|
||||
while (WSASend(m_sd, &wsaBuf, 1, &rv, 0, nullptr, nullptr) == SOCKET_ERROR) {
|
||||
if (WSAGetLastError() != WSAEWOULDBLOCK) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
if (!m_blocking) {
|
||||
*err = kWouldBlock;
|
||||
return 0;
|
||||
}
|
||||
Sleep(1);
|
||||
}
|
||||
if (!result) {
|
||||
char Buffer[128];
|
||||
#ifdef _MSC_VER
|
||||
sprintf_s(Buffer, "Send() failed: WSA error=%d\n", WSAGetLastError());
|
||||
#else
|
||||
std::snprintf(Buffer, sizeof(Buffer), "Send() failed: WSA error=%d\n",
|
||||
WSAGetLastError());
|
||||
#endif
|
||||
OutputDebugStringA(Buffer);
|
||||
*err = kConnectionReset;
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#ifdef MSG_NOSIGNAL
|
||||
// disable SIGPIPE on Linux
|
||||
ssize_t rv = ::send(m_sd, buffer, len, MSG_NOSIGNAL);
|
||||
#else
|
||||
ssize_t rv = ::send(m_sd, buffer, len, 0);
|
||||
#endif
|
||||
if (rv < 0) {
|
||||
if (!m_blocking && (errno == EAGAIN || errno == EWOULDBLOCK))
|
||||
*err = kWouldBlock;
|
||||
else
|
||||
*err = kConnectionReset;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
return static_cast<size_t>(rv);
|
||||
}
|
||||
|
||||
size_t TCPStream::receive(char* buffer, size_t len, Error* err, int timeout) {
|
||||
if (m_sd < 0) {
|
||||
*err = kConnectionClosed;
|
||||
return 0;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
int rv;
|
||||
#else
|
||||
ssize_t rv;
|
||||
#endif
|
||||
if (timeout <= 0) {
|
||||
#ifdef _WIN32
|
||||
rv = recv(m_sd, buffer, len, 0);
|
||||
#else
|
||||
rv = read(m_sd, buffer, len);
|
||||
#endif
|
||||
} else if (WaitForReadEvent(timeout)) {
|
||||
#ifdef _WIN32
|
||||
rv = recv(m_sd, buffer, len, 0);
|
||||
#else
|
||||
rv = read(m_sd, buffer, len);
|
||||
#endif
|
||||
} else {
|
||||
*err = kConnectionTimedOut;
|
||||
return 0;
|
||||
}
|
||||
if (rv < 0) {
|
||||
#ifdef _WIN32
|
||||
if (!m_blocking && WSAGetLastError() == WSAEWOULDBLOCK)
|
||||
#else
|
||||
if (!m_blocking && (errno == EAGAIN || errno == EWOULDBLOCK))
|
||||
#endif
|
||||
*err = kWouldBlock;
|
||||
else
|
||||
*err = kConnectionReset;
|
||||
return 0;
|
||||
}
|
||||
return static_cast<size_t>(rv);
|
||||
}
|
||||
|
||||
void TCPStream::close() {
|
||||
if (m_sd >= 0) {
|
||||
#ifdef _WIN32
|
||||
::shutdown(m_sd, SD_BOTH);
|
||||
closesocket(m_sd);
|
||||
#else
|
||||
::shutdown(m_sd, SHUT_RDWR);
|
||||
::close(m_sd);
|
||||
#endif
|
||||
}
|
||||
m_sd = -1;
|
||||
}
|
||||
|
||||
llvm::StringRef TCPStream::getPeerIP() const { return m_peerIP; }
|
||||
|
||||
int TCPStream::getPeerPort() const { return m_peerPort; }
|
||||
|
||||
void TCPStream::setNoDelay() {
|
||||
if (m_sd < 0) return;
|
||||
int optval = 1;
|
||||
setsockopt(m_sd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char*>(&optval),
|
||||
sizeof optval);
|
||||
}
|
||||
|
||||
bool TCPStream::setBlocking(bool enabled) {
|
||||
if (m_sd < 0) return true; // silently accept
|
||||
#ifdef _WIN32
|
||||
u_long mode = enabled ? 0 : 1;
|
||||
if (ioctlsocket(m_sd, FIONBIO, &mode) == SOCKET_ERROR) return false;
|
||||
#else
|
||||
int flags = fcntl(m_sd, F_GETFL, nullptr);
|
||||
if (flags < 0) return false;
|
||||
if (enabled)
|
||||
flags &= ~O_NONBLOCK;
|
||||
else
|
||||
flags |= O_NONBLOCK;
|
||||
if (fcntl(m_sd, F_SETFL, flags) < 0) return false;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
int TCPStream::getNativeHandle() const { return m_sd; }
|
||||
|
||||
bool TCPStream::WaitForReadEvent(int timeout) {
|
||||
fd_set sdset;
|
||||
struct timeval tv;
|
||||
|
||||
tv.tv_sec = timeout;
|
||||
tv.tv_usec = 0;
|
||||
FD_ZERO(&sdset);
|
||||
FD_SET(m_sd, &sdset);
|
||||
if (select(m_sd + 1, &sdset, NULL, NULL, &tv) > 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
170
wpiutil/src/main/native/cpp/udpsockets/UDPClient.cpp
Normal file
170
wpiutil/src/main/native/cpp/udpsockets/UDPClient.cpp
Normal file
@@ -0,0 +1,170 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2017-2018 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "udpsockets/UDPClient.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <WinSock2.h>
|
||||
#include <Ws2tcpip.h>
|
||||
#pragma comment(lib, "Ws2_32.lib")
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#include <fcntl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "support/Logger.h"
|
||||
#include "tcpsockets/SocketError.h"
|
||||
|
||||
using namespace wpi;
|
||||
|
||||
UDPClient::UDPClient(Logger& logger) : UDPClient("", logger) {}
|
||||
|
||||
UDPClient::UDPClient(llvm::StringRef address, Logger& logger)
|
||||
: m_lsd(0), m_address(address), m_logger(logger) {}
|
||||
|
||||
UDPClient::UDPClient(UDPClient&& other)
|
||||
: m_lsd(other.m_lsd),
|
||||
m_address(std::move(other.m_address)),
|
||||
m_logger(other.m_logger) {
|
||||
other.m_lsd = 0;
|
||||
}
|
||||
|
||||
UDPClient::~UDPClient() {
|
||||
if (m_lsd > 0) {
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
UDPClient& UDPClient::operator=(UDPClient&& other) {
|
||||
if (this == &other) return *this;
|
||||
shutdown();
|
||||
m_logger = other.m_logger;
|
||||
m_lsd = other.m_lsd;
|
||||
m_address = std::move(other.m_address);
|
||||
other.m_lsd = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int UDPClient::start() {
|
||||
if (m_lsd > 0) return 0;
|
||||
|
||||
#ifdef _WIN32
|
||||
WSAData wsaData;
|
||||
WORD wVersionRequested = MAKEWORD(2, 2);
|
||||
WSAStartup(wVersionRequested, &wsaData);
|
||||
#endif
|
||||
|
||||
m_lsd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
|
||||
if (m_lsd < 0) {
|
||||
WPI_ERROR(m_logger, "could not create socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct sockaddr_in addr;
|
||||
std::memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
if (m_address.size() > 0) {
|
||||
#ifdef _WIN32
|
||||
llvm::SmallString<128> addr_copy(m_address);
|
||||
addr_copy.push_back('\0');
|
||||
int res = InetPton(PF_INET, addr_copy.data(), &(addr.sin_addr));
|
||||
#else
|
||||
int res = inet_pton(PF_INET, m_address.c_str(), &(addr.sin_addr));
|
||||
#endif
|
||||
if (res != 1) {
|
||||
WPI_ERROR(m_logger, "could not resolve " << m_address << " address");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
}
|
||||
addr.sin_port = htons(0);
|
||||
|
||||
int result = bind(m_lsd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
|
||||
if (result != 0) {
|
||||
WPI_ERROR(m_logger, "bind() failed: " << SocketStrerror());
|
||||
return result;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void UDPClient::shutdown() {
|
||||
if (m_lsd > 0) {
|
||||
#ifdef _WIN32
|
||||
::shutdown(m_lsd, SD_BOTH);
|
||||
closesocket(m_lsd);
|
||||
WSACleanup();
|
||||
#else
|
||||
::shutdown(m_lsd, SHUT_RDWR);
|
||||
close(m_lsd);
|
||||
#endif
|
||||
m_lsd = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int UDPClient::send(llvm::ArrayRef<uint8_t> data, llvm::StringRef server,
|
||||
int port) {
|
||||
// server must be a resolvable IP address
|
||||
struct sockaddr_in addr;
|
||||
std::memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
if (server.size() > 0) {
|
||||
llvm::SmallVector<char, 128> addr_store;
|
||||
auto remoteAddr = server.c_str(addr_store);
|
||||
#ifdef _WIN32
|
||||
int res = InetPton(AF_INET, remoteAddr, &(addr.sin_addr));
|
||||
#else
|
||||
int res = inet_pton(AF_INET, remoteAddr, &(addr.sin_addr));
|
||||
#endif
|
||||
if (res != 1) {
|
||||
WPI_ERROR(m_logger, "could not resolve " << server << " address");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
WPI_ERROR(m_logger, "server must be passed");
|
||||
return -1;
|
||||
}
|
||||
addr.sin_port = htons(port);
|
||||
|
||||
// sendto should not block
|
||||
int result =
|
||||
sendto(m_lsd, reinterpret_cast<const char*>(data.data()), data.size(), 0,
|
||||
reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
|
||||
return result;
|
||||
}
|
||||
|
||||
int UDPClient::send(llvm::StringRef data, llvm::StringRef server, int port) {
|
||||
// server must be a resolvable IP address
|
||||
struct sockaddr_in addr;
|
||||
std::memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
if (server.size() > 0) {
|
||||
llvm::SmallVector<char, 128> addr_store;
|
||||
auto remoteAddr = server.c_str(addr_store);
|
||||
#ifdef _WIN32
|
||||
int res = InetPton(AF_INET, remoteAddr, &(addr.sin_addr));
|
||||
#else
|
||||
int res = inet_pton(AF_INET, remoteAddr, &(addr.sin_addr));
|
||||
#endif
|
||||
if (res != 1) {
|
||||
WPI_ERROR(m_logger, "could not resolve " << server << " address");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
WPI_ERROR(m_logger, "server must be passed");
|
||||
return -1;
|
||||
}
|
||||
addr.sin_port = htons(port);
|
||||
|
||||
// sendto should not block
|
||||
int result = sendto(m_lsd, data.data(), data.size(), 0,
|
||||
reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
|
||||
return result;
|
||||
}
|
||||
Reference in New Issue
Block a user