Index: include/clang-c/CXString.h =================================================================== --- include/clang-c/CXString.h +++ include/clang-c/CXString.h @@ -15,6 +15,7 @@ #define LLVM_CLANG_C_CXSTRING_H #include "clang-c/Platform.h" +#include #ifdef __cplusplus extern "C" { @@ -36,8 +37,11 @@ * with the string data, call \c clang_disposeString() to free the string. */ typedef struct { - const void *data; - unsigned private_flags; + const void *Contents; + unsigned Size; + bool IsNullTerminated; + bool IsOwned; + bool IsPooled; } CXString; typedef struct { @@ -47,9 +51,32 @@ /** * Retrieve the character data associated with the given string. + * + * This returns a pointer to a C string even if this \b CXString was built + * from e.g. a StringRef reference (which is not null-terminated). In those + * cases a string copy is performed on-demand. + * + * See also \b clang_getStringData and \b clang_getStringSize which could + * provide a range or "window" of data, if needed, and a C string is not + * strictly needed; this could save a C string allocation, deallocation, and + * string copy. */ CINDEX_LINKAGE const char *clang_getCString(CXString string); +/** + * Retrieve a pointer to the start of the character data for this string. + * + * Note that the string is not necessarily null-terminated, so it might not be + * suitable for use as a C string. See \b clang_getCString which guarantees + * a C string result. + */ +CINDEX_LINKAGE const char *clang_getStringData(CXString string); + +/** + * Retrieve the string length (not including null-terminator, if any). + */ +CINDEX_LINKAGE unsigned clang_getStringSize(CXString string); + /** * Free the given string. */ @@ -68,4 +95,3 @@ } #endif #endif - Index: tools/c-index-test/c-index-test.c =================================================================== --- tools/c-index-test/c-index-test.c +++ tools/c-index-test/c-index-test.c @@ -370,6 +370,33 @@ return CommentSchemaFile; } +static CXString createCXString(const char *CS) { + CXString Str; + + Str.Contents = (const void *) CS; + if (CS) { + Str.Size = strlen(CS); + Str.IsNullTerminated = true; + } else { + Str.Size = 0; + Str.IsNullTerminated = false; + } + Str.IsOwned = false; + Str.IsPooled = false; + return Str; +} + +static CXString copyToCXString(const char *OrigCS) { + CXString Str; + const char *CS; + + CS = strdup(OrigCS); + assert(CS); + Str = createCXString(CS); + Str.IsOwned = true; + return Str; +} + /******************************************************************************/ /* Pretty-printing. */ /******************************************************************************/ @@ -439,7 +466,7 @@ if (Version.Major < 0) return; printf("%s%d", Prefix, Version.Major); - + if (Version.Minor < 0) return; printf(".%d", Version.Minor); @@ -750,9 +777,7 @@ } assert(0 && "unknown display type"); /* no llvm_unreachable in C. */ /* Set to NULL to prevent uninitialized variable warnings. */ - text.data = NULL; - text.private_flags = 0; - return text; + return createCXString(NULL); } static void PrintCursor(CXCursor Cursor, const char *CommentSchemaFile) { @@ -797,10 +822,10 @@ CXSourceLocation Loc; if (I) printf(", "); - + Loc = clang_getCursorLocation(Ovl); clang_getSpellingLocation(Loc, 0, &line, &column, 0); - printf("%d:%d", line, column); + printf("%d:%d", line, column); } printf("]"); } else { @@ -822,15 +847,15 @@ if (clang_isCursorDefinition(Cursor)) printf(" (Definition)"); - + switch (clang_getCursorAvailability(Cursor)) { case CXAvailability_Available: break; - + case CXAvailability_Deprecated: printf(" (deprecated)"); break; - + case CXAvailability_NotAvailable: printf(" (unavailable)"); break; @@ -839,7 +864,7 @@ printf(" (inaccessible)"); break; } - + NumPlatformAvailability = clang_getCursorPlatformAvailability(Cursor, &AlwaysDeprecated, @@ -857,7 +882,7 @@ for (I = 0; I != NumPlatformAvailability; ++I) { if (I >= 2) break; - + printf(" (%s", clang_getCString(PlatformAvailability[I].Platform)); if (PlatformAvailability[I].Unavailable) printf(", unavailable"); @@ -877,7 +902,7 @@ break; clang_disposeCXPlatformAvailability(PlatformAvailability + I); } - + clang_disposeString(DeprecatedMessage); clang_disposeString(UnavailableMessage); @@ -964,7 +989,7 @@ printf(" [IBOutletCollection=%s]", clang_getCString(S)); clang_disposeString(S); } - + if (Cursor.kind == CXCursor_CXXBaseSpecifier) { enum CX_CXXAccessSpecifier access = clang_getCXXAccessSpecifier(Cursor); unsigned isVirtual = clang_isVirtualBase(Cursor); @@ -979,8 +1004,8 @@ accessStr = "protected"; break; case CX_CXXPrivate: accessStr = "private"; break; - } - + } + printf(" [access=%s isVirtual=%s]", accessStr, isVirtual ? "true" : "false"); } @@ -1026,7 +1051,7 @@ } clang_getOverriddenCursors(Cursor, &overridden, &num_overridden); - if (num_overridden) { + if (num_overridden) { unsigned I; LineCol lineCols[50]; assert(num_overridden <= 50); @@ -1047,28 +1072,28 @@ printf("]"); clang_disposeOverriddenCursors(overridden); } - + if (Cursor.kind == CXCursor_InclusionDirective) { CXFile File = clang_getIncludedFile(Cursor); CXString Included = clang_getFileName(File); printf(" (%s)", clang_getCString(Included)); clang_disposeString(Included); - + if (clang_isFileMultipleIncludeGuarded(TU, File)) printf(" [multi-include guarded]"); } - + CursorExtent = clang_getCursorExtent(Cursor); - RefNameRange = clang_getCursorReferenceNameRange(Cursor, + RefNameRange = clang_getCursorReferenceNameRange(Cursor, CXNameRange_WantQualifier | CXNameRange_WantSinglePiece | CXNameRange_WantTemplateArgs, 0); if (!clang_equalRanges(CursorExtent, RefNameRange)) PrintRange(RefNameRange, "SingleRefName"); - + for (RefNameRangeNr = 0; 1; RefNameRangeNr++) { - RefNameRange = clang_getCursorReferenceNameRange(Cursor, + RefNameRange = clang_getCursorReferenceNameRange(Cursor, CXNameRange_WantQualifier | CXNameRange_WantTemplateArgs, RefNameRangeNr); @@ -1149,28 +1174,24 @@ } } -static const char* GetCursorSource(CXCursor Cursor) { +static CXString GetCursorSource(CXCursor Cursor) { CXSourceLocation Loc = clang_getCursorLocation(Cursor); CXString source; CXFile file; + const char *sourceCStr; + CXString result; + clang_getExpansionLocation(Loc, &file, 0, 0, 0); source = clang_getFileName(file); - if (!clang_getCString(source)) { - clang_disposeString(source); - return ""; - } - else { - const char *b = basename(clang_getCString(source)); - clang_disposeString(source); - return b; + sourceCStr = clang_getCString(source); + if (!sourceCStr) { + result = createCXString(""); + } else { + result = copyToCXString(basename(sourceCStr)); } -} -static CXString createCXString(const char *CS) { - CXString Str; - Str.data = (const void *) CS; - Str.private_flags = 0; - return Str; + clang_disposeString(source); + return result; } /******************************************************************************/ @@ -1244,7 +1265,7 @@ PrintDiagnostic(Diag); if (ChildDiags) PrintDiagnosticSet(ChildDiags); - } + } } void PrintDiagnostics(CXTranslationUnit TU) { @@ -1267,7 +1288,7 @@ } fprintf(stderr, " TOTAL = %ld bytes (%f MBytes)\n", total, ((double) total)/(1024*1024)); - clang_disposeCXTUResourceUsage(usage); + clang_disposeCXTUResourceUsage(usage); } /******************************************************************************/ @@ -1290,13 +1311,17 @@ enum CXChildVisitResult FilteredPrintingVisitor(CXCursor Cursor, CXCursor Parent, CXClientData ClientData) { + CXString Source; + const char *SourceCStr; VisitorData *Data = (VisitorData *)ClientData; if (!Data->Filter || (Cursor.kind == *(enum CXCursorKind *)Data->Filter)) { CXSourceLocation Loc = clang_getCursorLocation(Cursor); unsigned line, column; clang_getSpellingLocation(Loc, 0, &line, &column, 0); - printf("// %s: %s:%d:%d: ", FileCheckPrefix, - GetCursorSource(Cursor), line, column); + Source = GetCursorSource(Cursor); + SourceCStr = clang_getCString(Source); + printf("// %s: %s:%d:%d: ", FileCheckPrefix, SourceCStr, line, column); + clang_disposeString(Source); PrintCursor(Cursor, Data->CommentSchemaFile); PrintCursorExtent(Cursor); if (clang_isDeclaration(Cursor.kind)) { @@ -1346,6 +1371,8 @@ CXSourceLocation Loc; CXFile file; CXString source; + CXString curSource; + const char *curSourceCStr; if (*startBuf == '\n') { startBuf++; @@ -1365,8 +1392,11 @@ if (Ref.kind == CXCursor_NoDeclFound) { /* Nothing found here; that's fine. */ } else if (Ref.kind != CXCursor_FunctionDecl) { - printf("// %s: %s:%d:%d: ", FileCheckPrefix, GetCursorSource(Ref), + curSource = GetCursorSource(Ref); + curSourceCStr = clang_getCString(curSource); + printf("// %s: %s:%d:%d: ", FileCheckPrefix, curSourceCStr, curLine, curColumn); + clang_disposeString(curSource); PrintCursor(Ref, Data->CommentSchemaFile); printf("\n"); } @@ -1384,7 +1414,10 @@ enum CXChildVisitResult USRVisitor(CXCursor C, CXCursor parent, CXClientData ClientData) { + CXString Source; + const char *SourceCStr; VisitorData *Data = (VisitorData *)ClientData; + if (!Data->Filter || (C.kind == *(enum CXCursorKind *)Data->Filter)) { CXString USR = clang_getCursorUSR(C); const char *cstr = clang_getCString(USR); @@ -1392,7 +1425,10 @@ clang_disposeString(USR); return CXChildVisit_Recurse; } - printf("// %s: %s %s", FileCheckPrefix, GetCursorSource(C), cstr); + Source = GetCursorSource(C); + SourceCStr = clang_getCString(Source); + printf("// %s: %s %s", FileCheckPrefix, SourceCStr, cstr); + clang_disposeString(Source); PrintCursorExtent(C); printf("\n"); @@ -1894,7 +1930,7 @@ /* Perform some simple filtering. */ if (!strcmp(filter, "all") || !strcmp(filter, "local")) ck = NULL; - else if (!strcmp(filter, "all-display") || + else if (!strcmp(filter, "all-display") || !strcmp(filter, "local-display")) { ck = NULL; wanted_display_type = DisplayType_DisplayName; @@ -2044,11 +2080,11 @@ int trial; int remap_after_trial = 0; char *endptr = 0; - + Idx = clang_createIndex(/* excludeDeclsFromPCH */ !strcmp(filter, "local") ? 1 : 0, /* displayDiagnostics=*/1); - + if (parse_remapped_files(argc, argv, 0, &unsaved_files, &num_unsaved_files)) { clang_disposeIndex(Idx); return -1; @@ -2062,7 +2098,7 @@ compiler_arg_idx = i+1; if (num_unsaved_files > compiler_arg_idx) compiler_arg_idx = num_unsaved_files; - + /* Load the initial translation unit -- we do this without honoring remapped * files, so that we have a way to test results after changing the source. */ Err = clang_parseTranslationUnit2(Idx, 0, @@ -2076,7 +2112,7 @@ clang_disposeIndex(Idx); return 1; } - + if (checkForErrors(TU) != 0) return -1; @@ -2105,13 +2141,13 @@ clang_disposeTranslationUnit(TU); free_remapped_files(unsaved_files, num_unsaved_files); clang_disposeIndex(Idx); - return -1; + return -1; } if (checkForErrors(TU) != 0) return -1; } - + result = perform_test_load(Idx, TU, filter, NULL, Visitor, PV, NULL); free_remapped_files(unsaved_files, num_unsaved_files); @@ -2363,7 +2399,7 @@ file); fprintf(file, "}"); continue; - } + } if (Kind == CXCompletionChunk_VerticalSpace) { fprintf(file, "{VerticalSpace }"); @@ -2647,7 +2683,7 @@ enum CXCursorKind containerKind; CXString objCSelector; const char *selectorString; - if (!timing_only) { + if (!timing_only) { /* Sort the code-completion results based on the typed text. */ clang_sortCodeCompletionResults(results->Results, results->NumResults); @@ -2660,39 +2696,39 @@ PrintDiagnostic(diag); clang_disposeDiagnostic(diag); } - + contexts = clang_codeCompleteGetContexts(results); print_completion_contexts(contexts, stdout); - + containerKind = clang_codeCompleteGetContainerKind(results, &containerIsIncomplete); - + if (containerKind != CXCursor_InvalidCode) { /* We have found a container */ CXString containerUSR, containerKindSpelling; containerKindSpelling = clang_getCursorKindSpelling(containerKind); printf("Container Kind: %s\n", clang_getCString(containerKindSpelling)); clang_disposeString(containerKindSpelling); - + if (containerIsIncomplete) { printf("Container is incomplete\n"); } else { printf("Container is complete\n"); } - + containerUSR = clang_codeCompleteGetContainerUSR(results); printf("Container USR: %s\n", clang_getCString(containerUSR)); clang_disposeString(containerUSR); } - + objCSelector = clang_codeCompleteGetObjCSelector(results); selectorString = clang_getCString(objCSelector); if (selectorString && strlen(selectorString) > 0) { printf("Objective-C selector: %s\n", selectorString); } clang_disposeString(objCSelector); - + clang_disposeCodeCompleteResults(results); } clang_disposeTranslationUnit(TU); @@ -2726,7 +2762,7 @@ unsigned NumLocations = 0, Loc; unsigned Repeats = 1; unsigned I; - + /* Count the number of locations. */ while (strstr(argv[NumLocations+1], locations_flag) == argv[NumLocations+1]) ++NumLocations; @@ -2782,7 +2818,7 @@ if (checkForErrors(TU) != 0) return -1; - + for (Loc = 0; Loc < NumLocations; ++Loc) { CXFile file = clang_getFile(TU, Locations[Loc].filename); if (!file) @@ -2801,7 +2837,7 @@ } } } - + PrintDiagnostics(TU); clang_disposeTranslationUnit(TU); clang_disposeIndex(CIdx); @@ -3020,7 +3056,7 @@ unsigned NumLocations = 0, Loc; unsigned Repeats = 1; unsigned I; - + /* Count the number of locations. */ while (strstr(argv[NumLocations+1], "-file-refs-at=") == argv[NumLocations+1]) ++NumLocations; @@ -3077,7 +3113,7 @@ if (checkForErrors(TU) != 0) return -1; - + for (Loc = 0; Loc < NumLocations; ++Loc) { CXFile file = clang_getFile(TU, Locations[Loc].filename); if (!file) @@ -3102,7 +3138,7 @@ } } } - + PrintDiagnostics(TU); clang_disposeTranslationUnit(TU); clang_disposeIndex(CIdx); @@ -3300,7 +3336,7 @@ unsigned line, column; const char *main_filename; int isMainFile; - + index_data = (IndexData *)client_data; clang_indexLoc_getFileLocation(loc, &file, 0, &line, &column, 0); if (line == 0) { @@ -3346,7 +3382,7 @@ char *newStr; CXIdxClientFile file; unsigned line, column; - + name = info->name; if (!name) name = ""; @@ -3526,8 +3562,8 @@ str = clang_formatDiagnostic(diag, clang_defaultDiagnosticDisplayOptions()); cstr = clang_getCString(str); printf("[diagnostic]: %s\n", cstr); - clang_disposeString(str); - + clang_disposeString(str); + if (getenv("CINDEXTEST_FAILONERROR") && clang_getDiagnosticSeverity(diag) >= CXDiagnostic_Error) { index_data->fail_for_error = 1; @@ -3565,7 +3601,7 @@ printCXIndexLoc(info->hashLoc, client_data); printf(" | isImport: %d | isAngled: %d | isModule: %d", info->isImport, info->isAngled, info->isModuleImport); - + Mod = clang_getModuleForFile(index_data->TU, (CXFile)info->file); if (Mod) { CXString str = clang_Module_getFullName(Mod); @@ -3590,7 +3626,7 @@ importedASTS_insert(index_data->importedASTs, clang_getCString(filename)); clang_disposeString(filename); } - + printf("[importedASTFile]: "); printCXIndexFile((CXIdxClientFile)info->file); if (info->module) { @@ -4460,9 +4496,9 @@ int num_unsaved_files = 0; enum CXErrorCode Err; int result = 0; - + Idx = clang_createIndex(/* excludeDeclsFromPCH */1, /* displayDiagnostics=*/1); - + if (parse_remapped_files(argc, argv, 0, &unsaved_files, &num_unsaved_files)) { clang_disposeIndex(Idx); return -1; @@ -4484,21 +4520,21 @@ return 1; } - switch (clang_saveTranslationUnit(TU, filename, + switch (clang_saveTranslationUnit(TU, filename, clang_defaultSaveOptions(TU))) { case CXSaveError_None: break; case CXSaveError_TranslationErrors: - fprintf(stderr, "Unable to write PCH file %s: translation errors\n", + fprintf(stderr, "Unable to write PCH file %s: translation errors\n", filename); - result = 2; + result = 2; break; case CXSaveError_InvalidTU: - fprintf(stderr, "Unable to write PCH file %s: invalid translation unit\n", + fprintf(stderr, "Unable to write PCH file %s: invalid translation unit\n", filename); - result = 3; + result = 3; break; case CXSaveError_Unknown: @@ -4507,7 +4543,7 @@ result = 1; break; } - + clang_disposeTranslationUnit(TU); free_remapped_files(unsaved_files, num_unsaved_files); clang_disposeIndex(Idx); @@ -4587,7 +4623,7 @@ CXSourceRange ReplacementRange; CXString text; text = clang_getDiagnosticFixIt(D, i, &ReplacementRange); - + printIndent(indent); fprintf(stderr, "FIXIT: ("); printLocation(clang_getRangeStart(ReplacementRange)); @@ -4595,7 +4631,7 @@ printLocation(clang_getRangeEnd(ReplacementRange)); fprintf(stderr, "): \"%s\"\n", clang_getCString(text)); clang_disposeString(text); - } + } } static void printDiagnosticSet(CXDiagnosticSet Diags, unsigned indent) { @@ -4603,7 +4639,7 @@ if (!Diags) return; - + n = clang_getNumDiagnosticsInSet(Diags); for (i = 0; i < n; ++i) { CXSourceLocation DiagLoc; @@ -4612,15 +4648,15 @@ CXString FileName, DiagSpelling, DiagOption, DiagCat; unsigned line, column, offset; const char *DiagOptionStr = 0, *DiagCatStr = 0; - + D = clang_getDiagnosticInSet(Diags, i); DiagLoc = clang_getDiagnosticLocation(D); clang_getExpansionLocation(DiagLoc, &File, &line, &column, &offset); FileName = clang_getFileName(File); DiagSpelling = clang_getDiagnosticSpelling(D); - + printIndent(indent); - + fprintf(stderr, "%s:%d:%d: %s: %s", clang_getCString(FileName), line, @@ -4633,18 +4669,18 @@ if (DiagOptionStr) { fprintf(stderr, " [%s]", DiagOptionStr); } - + DiagCat = clang_getDiagnosticCategoryText(D); DiagCatStr = clang_getCString(DiagCat); if (DiagCatStr) { fprintf(stderr, " [%s]", DiagCatStr); } - + fprintf(stderr, "\n"); - + printRanges(D, indent); printFixIts(D, indent); - + /* Print subdiagnostics. */ printDiagnosticSet(clang_getChildDiagnostics(D), indent+2); @@ -4652,14 +4688,14 @@ clang_disposeString(DiagSpelling); clang_disposeString(DiagOption); clang_disposeString(DiagCat); - } + } } static int read_diagnostics(const char *filename) { enum CXLoadDiag_Error error; CXString errorString; CXDiagnosticSet Diags = 0; - + Diags = clang_loadDiagnostics(filename, &error, &errorString); if (!Diags) { fprintf(stderr, "Trouble deserializing file (%s): %s\n", @@ -4668,7 +4704,7 @@ clang_disposeString(errorString); return 1; } - + printDiagnosticSet(Diags, 0); fprintf(stderr, "Number of diagnostics: %d\n", clang_getNumDiagnosticsInSet(Diags)); @@ -4797,17 +4833,17 @@ CXCursorVisitor I = GetVisitor(argv[1] + 25); if (I) { int trials = atoi(argv[2]); - return perform_test_reparse_source(argc - 4, argv + 4, trials, argv[3], I, + return perform_test_reparse_source(argc - 4, argv + 4, trials, argv[3], I, NULL); } } else if (argc >= 4 && strncmp(argv[1], "-test-load-source", 17) == 0) { CXCursorVisitor I = GetVisitor(argv[1] + 17); - + PostVisitTU postVisit = 0; if (strstr(argv[1], "-memory-usage")) postVisit = PrintMemoryUsage; - + if (I) return perform_test_load_source(argc - 3, argv + 3, argv[2], I, postVisit); Index: tools/libclang/CXString.cpp =================================================================== --- tools/libclang/CXString.cpp +++ tools/libclang/CXString.cpp @@ -15,99 +15,130 @@ #include "CXString.h" #include "CXTranslationUnit.h" +#include "clang-c/CXString.h" #include "clang-c/Index.h" #include "clang/Frontend/ASTUnit.h" #include "llvm/Support/ErrorHandling.h" +#include using namespace clang; -/// Describes the kind of underlying data in CXString. -enum CXStringFlag { - /// CXString contains a 'const char *' that it doesn't own. - CXS_Unmanaged, - - /// CXString contains a 'const char *' that it allocated with malloc(). - CXS_Malloc, - - /// CXString contains a CXStringBuf that needs to be returned to the - /// CXStringPool. - CXS_StringBuf -}; +static_assert(sizeof(CXString) <= 16, ""); namespace clang { namespace cxstring { +/** + * This is for \b CXString 's which are created with \b CreateRef(StringRef). + * We'll store the info from the input \b StringRef: char ptr and size. + * + * We don't know for sure whether this is null-terminated so, when and if + * \b clang_getCString is called for this \b CXString, we'll allocate C string + * storage and copy data into the storage. We'll memo-ize that in the + * \b CString member. + * + * This is refcounted; the \b Count member is initially 1. When a \b CXString + * instance using this object is disposed via \b clang_disposeString, \b Count + * is decremented. When this string is duplicated the \b Count increases. + * + * When \b Count finally drops to zero, the ptr at \b CString, and this object, + * should be deleted. + */ +struct RefCountedCharRange { + const char *Data; + const char *CString; + unsigned Size; + unsigned Count; +}; + //===----------------------------------------------------------------------===// // Basic generation of CXStrings. //===----------------------------------------------------------------------===// -CXString createEmpty() { +CXString createRef(const char *String) { CXString Str; - Str.data = ""; - Str.private_flags = CXS_Unmanaged; + Str.Contents = (const void *) String; + if (String) { + Str.Size = strlen(String); + Str.IsNullTerminated = true; + } else { + Str.Size = 0; + Str.IsNullTerminated = false; + } + Str.IsOwned = false; + Str.IsPooled = false; return Str; } -CXString createNull() { - CXString Str; - Str.data = nullptr; - Str.private_flags = CXS_Unmanaged; - return Str; +CXString createEmpty() { + return createRef(""); } -CXString createRef(const char *String) { - if (String && String[0] == '\0') - return createEmpty(); +CXString createNull() { + return createRef(nullptr); +} - CXString Str; - Str.data = String; - Str.private_flags = CXS_Unmanaged; - return Str; +inline static const char *copyCharRange(const char *CS, unsigned Size) { + char *Spelling = (char *) malloc(Size + 1); + assert(Spelling); + if (CS) { + memcpy(Spelling, CS, Size); + } + Spelling[Size] = 0; + return Spelling; } -CXString createDup(const char *String) { - if (!String) + CXString createDup(const char *String) { + if (!String) { return createNull(); - - if (String[0] == '\0') + } + if (String[0] == '\0') { return createEmpty(); + } CXString Str; - Str.data = strdup(String); - Str.private_flags = CXS_Malloc; + Str.Size = strlen(String); + Str.Contents = (const void *) copyCharRange(String, Str.Size); + Str.IsNullTerminated = true; + Str.IsOwned = true; + Str.IsPooled = false; return Str; } CXString createRef(StringRef String) { - // If the string is not nul-terminated, we have to make a copy. - - // FIXME: This is doing a one past end read, and should be removed! For memory - // we don't manage, the API string can become unterminated at any time outside - // our control. - - if (!String.empty() && String.data()[String.size()] != 0) - return createDup(String); - - CXString Result; - Result.data = String.data(); - Result.private_flags = (unsigned) CXS_Unmanaged; - return Result; + assert (String.size() <= std::numeric_limits::max()); + CXString Str; + Str.Size = unsigned(String.size()); + Str.IsNullTerminated = false; + Str.IsOwned = false; + Str.IsPooled = false; + auto *RC = new RefCountedCharRange { + /* Data */ String.data(), + /* CString */ nullptr, + /* Size */ Str.Size, + /* Count */ 1, + }; + Str.Contents = (const void *) RC; + return Str; } CXString createDup(StringRef String) { - CXString Result; - char *Spelling = static_cast(llvm::safe_malloc(String.size() + 1)); - memmove(Spelling, String.data(), String.size()); - Spelling[String.size()] = 0; - Result.data = Spelling; - Result.private_flags = (unsigned) CXS_Malloc; - return Result; + CXString Str; + Str.Size = String.size(); + Str.Contents = (const void *) copyCharRange(String.data(), Str.Size); + Str.IsNullTerminated = true; + Str.IsOwned = true; + Str.IsPooled = false; + return Str; } CXString createCXString(CXStringBuf *buf) { CXString Str; - Str.data = buf; - Str.private_flags = (unsigned) CXS_StringBuf; + Str.Contents = buf->Data.data(); + Str.Size = buf->Data.size(); + Str.IsNullTerminated = true; + Str.IsOwned = false; + Str.IsPooled = true; return Str; } @@ -151,7 +182,7 @@ } bool isManagedByPool(CXString str) { - return ((CXStringFlag) str.private_flags) == CXS_StringBuf; + return str.IsPooled; } } // end namespace cxstring @@ -161,25 +192,64 @@ // libClang public APIs. //===----------------------------------------------------------------------===// +using namespace clang::cxstring; + const char *clang_getCString(CXString string) { - if (string.private_flags == (unsigned) CXS_StringBuf) { - return static_cast(string.data)->Data.data(); + const char *CString = nullptr; + + // If not null-terminated, then this is a reference to a range of characters + // owned elsewhere. We'll need to copy this to a new C string to ensure it + // is null-terminated, if we haven't done so already. + if (string.IsNullTerminated) { + CString = (const char *) string.Contents; + } else { + auto *V = const_cast(string.Contents); + if (V) { + auto *R = (RefCountedCharRange *) V; + if (R) { + if (!R->CString) { + R->CString = copyCharRange(R->Data, R->Size); + } + CString = R->CString; + } + } } - return static_cast(string.data); + + return CString; +} + +const char *clang_getStringData(CXString string) { + if (!(string.IsOwned || string.IsNullTerminated)) { + auto *R = (const RefCountedCharRange *) string.Contents; + return R ? R->Data : nullptr; + } + return (const char *) string.Contents; +} + +unsigned clang_getStringSize(CXString string) { + return string.Size; } void clang_disposeString(CXString string) { - switch ((CXStringFlag) string.private_flags) { - case CXS_Unmanaged: - break; - case CXS_Malloc: - if (string.data) - free(const_cast(string.data)); - break; - case CXS_StringBuf: - static_cast( - const_cast(string.data))->dispose(); - break; + if (string.IsOwned) { + // Is a C string that we own; need to free it. + const void *Chars = (const void *) string.Contents; + free(const_cast(Chars)); + } else { + if (!string.IsNullTerminated) { + // Neither null-terminated nor owned. Is a char range for which we might + // have created a C string. + auto *CR = (const RefCountedCharRange *) string.Contents; + auto *R = const_cast(CR); + if (R) { + -- R->Count; + if (!R->Count) { + const void *CStr = (const void *) R->CString; + free(const_cast(CStr)); + delete R; + } + } + } } } @@ -189,4 +259,3 @@ delete[] set->Strings; delete set; } -