diff --git a/.gitattributes b/.gitattributes deleted file mode 100755 index 1ff0c42..0000000 --- a/.gitattributes +++ /dev/null @@ -1,63 +0,0 @@ -############################################################################### -# Set default behavior to automatically normalize line endings. -############################################################################### -* text=auto - -############################################################################### -# Set default behavior for command prompt diff. -# -# This is need for earlier builds of msysgit that does not have it on by -# default for csharp files. -# Note: This is only used by command line -############################################################################### -#*.cs diff=csharp - -############################################################################### -# Set the merge driver for project and solution files -# -# Merging from the command prompt will add diff markers to the files if there -# are conflicts (Merging from VS is not affected by the settings below, in VS -# the diff markers are never inserted). Diff markers may cause the following -# file extensions to fail to load in VS. An alternative would be to treat -# these files as binary and thus will always conflict and require user -# intervention with every merge. To do so, just uncomment the entries below -############################################################################### -#*.sln merge=binary -#*.csproj merge=binary -#*.vbproj merge=binary -#*.vcxproj merge=binary -#*.vcproj merge=binary -#*.dbproj merge=binary -#*.fsproj merge=binary -#*.lsproj merge=binary -#*.wixproj merge=binary -#*.modelproj merge=binary -#*.sqlproj merge=binary -#*.wwaproj merge=binary - -############################################################################### -# behavior for image files -# -# image files are treated as binary by default. -############################################################################### -#*.jpg binary -#*.png binary -#*.gif binary - -############################################################################### -# diff behavior for common document formats -# -# Convert binary document formats to text before diffing them. This feature -# is only available from the command line. Turn it on by uncommenting the -# entries below. -############################################################################### -#*.doc diff=astextplain -#*.DOC diff=astextplain -#*.docx diff=astextplain -#*.DOCX diff=astextplain -#*.dot diff=astextplain -#*.DOT diff=astextplain -#*.pdf diff=astextplain -#*.PDF diff=astextplain -#*.rtf diff=astextplain -#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore deleted file mode 100644 index eab024c..0000000 --- a/.gitignore +++ /dev/null @@ -1,40 +0,0 @@ -*.o -*.so -*.a -*.dll -*.DLL -test -testmain -VisualStudio/*.user -VisualStudio/Debug/ -VisualStudio/Release/ -*.opensdf -*.sdf -*.suo -bin/ -libs/ -gen/ -obj/ -*.apk -*.ap_ -*.dex -*.class -local.properties -*.pydevproject -.project -.metadata -bin/** -tmp/** -tmp/**/* -*.tmp -*.bak -*.swp -*~.nib -local.properties -.classpath -.settings/ -.loadpath -.externalToolBuilders/ -*.launch -.cproject -.buildpath diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..24813d8 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,69 @@ +sudo: required +language: cpp +dist: trusty + +matrix: + include: + + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-8 + env: + - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" + + - os: osx + osx_image: xcode9.2 + compiler: clang + +before_install: + # Make sure we set correct env for platform + - eval "$(MATRIX_EVAL)" + + # Workaround for Travis CI macOS bug (https://github.com/travis-ci/travis-ci/issues/6307) + # See https://github.com/searchivarius/nmslib/pull/259 + - | + if [ "${TRAVIS_OS_NAME}" == "osx" ]; then + command curl -sSL https://rvm.io/mpapis.asc | gpg --import -; + rvm get head || true + fi + +script: + - export CHECKOUT_PATH=`pwd`; + - echo "ROOT_PATH= $ROOT_PATH" + - echo "CHECKOUT_PATH= $CHECKOUT_PATH" + + ####################################################################################### + # Install a recent CMake (unless already installed on OS X) + ####################################################################################### + - | + if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then + CMAKE_URL="http://www.cmake.org/files/v3.10/cmake-3.10.2-Linux-x86_64.tar.gz" + mkdir cmake && travis_retry wget --no-check-certificate --quiet -O - ${CMAKE_URL} | tar --strip-components=1 -xz -C cmake + export PATH=${DEPS_DIR}/cmake/bin:${PATH} + else + brew upgrade cmake || echo "suppress failures in order to ignore warnings" + brew link --overwrite cmake + fi + - cmake --version + + - | + if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then + sudo apt-get install uuid-dev + fi + ####################################################################################### + # Build the library + ####################################################################################### + - | + cd "${CHECKOUT_PATH}" + mkdir build + cd build + cmake .. -DCMAKE_BUILD_TYPE="Debug" + sudo make install + ####################################################################################### + # Run the tests + ####################################################################################### + - ./crossguid-test diff --git a/.vscode/.cmaketools.json b/.vscode/.cmaketools.json new file mode 100644 index 0000000..e8a5846 --- /dev/null +++ b/.vscode/.cmaketools.json @@ -0,0 +1,5 @@ +{ + "variant": null, + "activeEnvironments": [], + "codeModel": null +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..76b5a62 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,97 @@ +cmake_minimum_required(VERSION 3.5.1) +project(CrossGuid VERSION 0.2.3) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake") + +option(CROSSGUID_TESTS "Build test runner" ON) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_DEBUG_POSTFIX "-dgb") + +# Set the build type if not set +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release") +endif() + +add_library(crossguid ${CMAKE_CURRENT_SOURCE_DIR}/src/guid.cpp) +set_property(TARGET crossguid PROPERTY POSITION_INDEPENDENT_CODE ON) +target_include_directories(crossguid PUBLIC + $ + $) + +if(WIN32) + target_compile_definitions(crossguid PRIVATE GUID_WINDOWS) +elseif(APPLE) + find_library(CFLIB CoreFoundation) + target_link_libraries(crossguid ${CFLIB}) + target_compile_definitions(crossguid PRIVATE GUID_CFUUID) +elseif(ANDROID) + # GUID_ANDROID is used in the headers, so make PUBLIC + target_compile_definitions(crossguid PUBLIC GUID_ANDROID) +else() + find_package(Libuuid REQUIRED) + if (NOT LIBUUID_FOUND) + message(FATAL_ERROR + "You might need to run 'sudo apt-get install uuid-dev' or similar") + endif() + target_include_directories(crossguid PRIVATE ${LIBUUID_INCLUDE_DIR}) + target_link_libraries(crossguid ${LIBUUID_LIBRARY}) + target_compile_definitions(crossguid PRIVATE GUID_LIBUUID) +endif() + +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(WARNINGS "-Werror" "-Wall") +elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(WARNINGS "-Werror" "-Wall") +elseif(MSVC) + set(WARNINGS "/WX" "/W4") +endif() +target_compile_options(crossguid PRIVATE ${WARNINGS}) + +set_target_properties(crossguid + PROPERTIES + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR} + DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX}) + +if (${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR}) + include(GNUInstallDirs) + + set(CROSSGUID_INC_INSTALL_DIR "${CMAKE_INSTALL_INCLUDEDIR}") + set(CROSSGUID_RUNTIME_INSTALL_DIR "${CMAKE_INSTALL_BINDIR}") + set(CROSSGUID_LIBRARY_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}") + set(CROSSGUID_ARCHIVE_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}") + set(CROSSGUID_FRAMEWORK_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}") + + set(CROSSGUID_CMAKE_CONFIG_INSTALL_DIR "${CMAKE_INSTALL_DATADIR}/crossguid/cmake") + set(CROSSGUID_ADDITIONAL_FILES_INSTALL_DIR "${CMAKE_INSTALL_DATADIR}/crossguid") + + # Install target + install(TARGETS crossguid EXPORT crossguidTargets + RUNTIME DESTINATION ${CROSSGUID_RUNTIME_INSTALL_DIR} + LIBRARY DESTINATION ${CROSSGUID_LIBRARY_INSTALL_DIR} + ARCHIVE DESTINATION ${CROSSGUID_ARCHIVE_INSTALL_DIR} + FRAMEWORK DESTINATION ${CROSSGUID_FRAMEWORK_INSTALL_DIR}) + + # Install headers + install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/" + DESTINATION ${CROSSGUID_INC_INSTALL_DIR}) + + # Make cmake config files for all targets + install(EXPORT crossguidTargets + DESTINATION ${CROSSGUID_CMAKE_CONFIG_INSTALL_DIR} + FILE crossguid-config.cmake) + + # Install readme and license + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE" "${CMAKE_CURRENT_SOURCE_DIR}/README.md" + DESTINATION ${CROSSGUID_ADDITIONAL_FILES_INSTALL_DIR}) + + configure_file(crossguid.pc.in ${PROJECT_BINARY_DIR}/crossguid.pc @ONLY) + install(FILES ${PROJECT_BINARY_DIR}/crossguid.pc DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}/pkgconfig) +endif() + +if (CROSSGUID_TESTS) + add_executable(crossguid-test test/TestMain.cpp test/Test.cpp) + target_link_libraries(crossguid-test crossguid) +endif() diff --git a/README.md b/README.md index a06c376..44c6164 100644 --- a/README.md +++ b/README.md @@ -1,60 +1,131 @@ -# CrossGuid +# CrossGuid [![Build Status](https://travis-ci.org/graeme-hill/crossguid.svg?branch=master)](https://travis-ci.org/graeme-hill/crossguid) CrossGuid is a minimal, cross platform, C++ GUID library. It uses the best -native GUID/UUID generator on the given platform and had a generic class for -parsing, stringifying, and comparing IDs. The intention is that anyone who -uses this code can simply copy `guid.h` and `guid.cpp` into their project and -define one of the following preprocessor flags to control the implementation: - -* `GUID_LIBUUID` - Uses `libuuid` which is normally used on linux but possibly - usable elsewhere as well. -* `GUID_CFUUID` - Uses `CFCreateUUID` from Apple's `CoreFoundation` framework. - This works on both Mac OSX and iOS. -* `GUID_WINDOWS` - Uses the built in `CoCreateGuid` function in Windows. -* `GUID_ANDROID` - Uses JNI to invoke Java functions from Android SDK. - -I recommend taking the time to actually look at the `guid.h` and `guid.cpp` so -that you can see how simple they are. It should be pretty trivial to modify -the code to match your naming conventions or drop it into a namespace so that it -fits in nicely with your code base. +native GUID/UUID generator on the given platform and has a generic class for +parsing, stringifying, and comparing IDs. The guid generation technique is +determined by your platform: + +## Linux + +On linux you can use `libuuid` which is pretty standard. On distros like Ubuntu +it is available by default but to use it you need the header files so you have +to do: + + sudo apt-get install uuid-dev + +## Mac/iOS + +On Mac or iOS you can use `CFUUIDCreate` from `CoreFoundation`. Since it's a +plain C function you don't even need to compile as Objective-C++. + +## Windows + +On Windows we just use the the built-in function `CoCreateGuid`. CMake can +generate a Visual Studio project if that's your thing. + +## Android + +The Android version uses a handle to a `JNIEnv` object to invoke the +`randomUUID()` function on `java.util.UUID` from C++. The Android specific code +is all in the `android/` subdirectory. If you have an emulator already running, +then you can run the `android.sh` script in the root directory. It has the +following requirements: + +- Android emulator is already running (or you have physical device connected). +- You're using bash. +- adb is in your path. +- You have an Android sdk setup including `ANDROID_HOME` environment variable. + +## Versions + +This is version 0.2 of CrossGuid. If you all already using CrossGuid and your code +uses `GuidGenerator` then you are using version 0.1. Differences in version 0.2: + +- Put everything inside the namespace `xg` instead of using the global + namespace. +- Removed `GuidGenerator` class and replaced with the free function + `xg::newGuid`. This is the way I originally wanted it to work but since Android + is a special snowflake requiring state (`JNIEnv *`) I introduced the + `GuidGenerator` class specifically so that there would be somewhere to store + the `JNIEnv *` when running on Android. However, this basically meant + complicating the library for the sake of one platform. In version 0.2 the goal is + to design for the normal platforms and let Android be weird. In Android you just + need to run `xg::initJni(JNIEnv *)` before you create any guids. The `JNIEnv *` + is just stored as a global variable. +- Added CMake build system. Instead of different scripts for each platform you + can just run cmake and it should handle each platform (except Android which + again is special). +- Actual guid bytes are stored in `std::array` instead of + `std::vector`. +- More error checking (like if you try to create a guid with invalid number of + bytes). + +If you're happily using version 0.1 then there's not really any reason to +change. + +## Compiling + +Just do the normal cmake thing: + +``` +mkdir build +cd build +cmake .. +make install +``` + +## Running tests + +After compiling as described above you should get two files: `libcrossguid.a` (the +static library) and `crossguid-test` (the test runner). So to run the tests just do: + +``` +./crossguid-test +``` ## Basic usage -### Tests - -The tests are a simple way to figure out how the library works. There is a file -in the root of the repository called `test.cpp` that runs a simple set of tests -and outputs a few guid strings for a sanity check. This file does not have a -`main()` function entry point there, it is intended to be called from somewhere -else, and it takes a `GuidGenerator` as input. All platforms except for Android -use `testmain.cpp` to construct a `GuidGenerator` and run the tests. In Android -there is a special file called `android/jni/jnitest.cpp` which invokes the -tests. - -### Creating a guid generator - -Creation of a guid generator is not exactly the same in every platform, but -this is an intentional feature. In Android the guid generation function has to -have access to a `JNIEnv` handle, but that handle is not necessarily the same -all the time. Therefore, there is a `GuidGenerator` class whose construction is -different depending on the platform, but client code can pass around a -`GuidGenerator` object and then use it the same on every platform. On every -platform except Android, you can create a guid generator like this: - - GuidGenerator generator; - -But on Android you need to pass a `JNIEnv *`: - - GuidGenerator generator(env); - ### Creating guids -On every platform guid creation is the same: - - void doGuidStuff(GuidGenerator generator) - { - auto myGuid = generator.newGuid(); - } +Create a new random guid: + +```cpp +#include +... +auto g = xg::newGuid(); +``` + +**NOTE:** On Android you need to call `xg::initJni(JNIEnv *)` first so that it +is possible for `xg::newGuid()` to call back into java libraries. `initJni` +only needs to be called once when the process starts. + +Create a new zero guid: + +```cpp +xg::Guid g; +``` + +Create from a string: + +```cpp +xg::Guid g("c405c66c-ccbb-4ffd-9b62-c286c0fd7a3b"); +``` + +### Checking validity + +If you have some string value and you need to check whether it is a valid guid +then you can simply attempt to construct the guid: + +```cpp +xg::Guid g("bad-guid-string"); +if (!g.isValid()) +{ + // do stuff +} +``` + +If the guid string is not valid then all bytes are set to zero and `isValid()` +returns `false`. ### Converting guid to string @@ -65,32 +136,31 @@ utilize strings because the `<<` operator is overloaded. To print a guid to `std::cout`: - - void doGuidStuff(GuidGenerator generator) - { - auto myGuid = generator.newGuid(); - std::cout << "Here is a guid: " << myGuid << std::endl; - } +```cpp +void doGuidStuff() +{ + auto myGuid = xg::newGuid(); + std::cout << "Here is a guid: " << myGuid << std::endl; +} +``` Or to store a guid in a `std::string`: - void doGuidStuff(GuidGenerator generator) - { - auto myGuid = generator.newGuid(); - std::stringstring stream; - stream << myGuid; - auto guidString = stream.str(); - } - -### Parsing a string into a guid - -There is a constructor that can be used to create a guid from a string without -needing any reference to a `GuidGenerator`: - - void doGuidStuff() - { - Guid guid("e63e03a8-f3e5-4e0f-99bb-a3fc402d4fc8"); - } +```cpp +void doGuidStuff() +{ + auto myGuid = xg::newGuid(); + std::stringstream stream; + stream << myGuid; + auto guidString = stream.str(); +} +``` + +There is also a `str()` function that returns a `std::string`: + +```cpp +std::string guidStr = xg::newGuid().str(); +``` ### Creating a guid from raw bytes @@ -98,75 +168,37 @@ internally to construct a `Guid` object from the raw data given by the system's built-in guid generation function. There are two key constructors for this: - Guid(const vector &bytes); +```cpp +Guid(std::array &bytes); +``` and - Guid(const unsigned char *bytes); - -In both cases the constructor expects to receive exactly 16 bytes. +```cpp +Guid(const unsigned char * bytes); +``` + +When possible prefer the `std::array` constructor because it is safer. If you +pass in an incorrectly sized C array then bad things will happen. ### Comparing guids `==` and `!=` are implemented, so the following works as expected: - void doGuidStuff(GuidGenerator generator) - { - auto guid1 = generator.newGuid(); - auto guid2 = generator.newGuid(); - - auto guidsAreEqual = guid1 == guid2; - auto guidsAreNotEqual = guid1 != guid2; - } - -## Linux - -**The Linux version uses the proprocessor flag `GUID_LIBUUID`** - -On linux you can use libuuid which is pretty standard. On distros like Ubuntu -it is available by default but to use it you need the header files so you have -to do: - - sudo apt-get install uuid-dev - -Then you can compile and run tests with: - - ./linux.sh - -## Mac/iOS - -**The Mac and iOS versions use the preprocessor flag `GUID_CFUUID`** - -On Mac or iOS you can use `CFUUIDCreate` from `CoreFoundation`. Since it's a -plain C function you don't even need to compile as Objective-C++. If you have -the command line tools that come with Xcode installed, then you can compile and -run the tests like this: - - ./mac.sh - -## Windows - -**The Windows version uses the preprocessor flag `GUID_WINDOWS`** - -On Windows we just the the built-in function `CoCreateGuid`. There is a Visual -Studio 2013 solution in the `VisualStudio` directory which you can use to -compile and run tests. - -## Android - -**The Android version uses the preprocessor flag `GUID_ANDROID`** - -The Android version uses a handle to a `JNIEnv` object to invoke the -`randomUUID()` function on `java.util.UUID` from C++. The Android specific code -is all in the `android/` subdirectory. If you have an emulator already running, -then you can run the `android.sh` script in the root directory. It has the -following requirements: - -* Android emulator is already running (or you have physical device connected). -* You're using bash. -* adb is in your path. -* ndk-build and other ndk cross-compile tools are in your path. -* You have a jdk setup including `JAVA_HOME` environment variable. +```cpp +void doGuidStuff() +{ + auto guid1 = xg::newGuid(); + auto guid2 = xg::newGuid(); + + auto guidsAreEqual = guid1 == guid2; + auto guidsAreNotEqual = guid1 != guid2; +} +``` + +### Hashing guids + +Guids can be used directly in containers requireing `std::hash` such as `std::map,`std::unordered_map` etc. ## License diff --git a/VisualStudio/CrossGuid-Test.vcxproj b/VisualStudio/CrossGuid-Test.vcxproj deleted file mode 100644 index ba334e4..0000000 --- a/VisualStudio/CrossGuid-Test.vcxproj +++ /dev/null @@ -1,93 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {C8233271-F319-4531-AEF6-3BD6420B55CD} - Win32Proj - CrossGuid - CrossGuid-Test - - - - Application - true - v120 - Unicode - - - Application - false - v120 - true - Unicode - - - - - - - - - - - - - true - - - false - - - - - - Level3 - Disabled - WIN32;_DEBUG;_CONSOLE;_LIB;GUID_WINDOWS;%(PreprocessorDefinitions) - - - Console - true - CrossGuidd.lib;%(AdditionalDependencies) - $(SolutionDir)$(Configuration) - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;_LIB;GUID_WINDOWS;%(PreprocessorDefinitions) - - - Console - true - true - true - CrossGuid.lib;%(AdditionalDependencies) - $(SolutionDir)$(Configuration) - - - - - - - - - - - - - \ No newline at end of file diff --git a/VisualStudio/CrossGuid-Test.vcxproj.filters b/VisualStudio/CrossGuid-Test.vcxproj.filters deleted file mode 100644 index 6aa0572..0000000 --- a/VisualStudio/CrossGuid-Test.vcxproj.filters +++ /dev/null @@ -1,30 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Source Files - - - Source Files - - - - - Header Files - - - \ No newline at end of file diff --git a/VisualStudio/CrossGuid.sln b/VisualStudio/CrossGuid.sln deleted file mode 100755 index 7e2e8c3..0000000 --- a/VisualStudio/CrossGuid.sln +++ /dev/null @@ -1,31 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Express 2013 for Windows Desktop -VisualStudioVersion = 12.0.40629.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CrossGuid-Test", "CrossGuid-Test.vcxproj", "{C8233271-F319-4531-AEF6-3BD6420B55CD}" - ProjectSection(ProjectDependencies) = postProject - {AF365E5F-E35D-40A6-958A-C7B6988C994D} = {AF365E5F-E35D-40A6-958A-C7B6988C994D} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CrossGuid", "CrossGuid.vcxproj", "{AF365E5F-E35D-40A6-958A-C7B6988C994D}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C8233271-F319-4531-AEF6-3BD6420B55CD}.Debug|Win32.ActiveCfg = Debug|Win32 - {C8233271-F319-4531-AEF6-3BD6420B55CD}.Debug|Win32.Build.0 = Debug|Win32 - {C8233271-F319-4531-AEF6-3BD6420B55CD}.Release|Win32.ActiveCfg = Release|Win32 - {C8233271-F319-4531-AEF6-3BD6420B55CD}.Release|Win32.Build.0 = Release|Win32 - {AF365E5F-E35D-40A6-958A-C7B6988C994D}.Debug|Win32.ActiveCfg = Debug|Win32 - {AF365E5F-E35D-40A6-958A-C7B6988C994D}.Debug|Win32.Build.0 = Debug|Win32 - {AF365E5F-E35D-40A6-958A-C7B6988C994D}.Release|Win32.ActiveCfg = Release|Win32 - {AF365E5F-E35D-40A6-958A-C7B6988C994D}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/VisualStudio/CrossGuid.vcxproj b/VisualStudio/CrossGuid.vcxproj deleted file mode 100755 index b9145c3..0000000 --- a/VisualStudio/CrossGuid.vcxproj +++ /dev/null @@ -1,79 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - - - - - - - {AF365E5F-E35D-40A6-958A-C7B6988C994D} - CrossGuid - - - - StaticLibrary - true - v120 - Unicode - - - StaticLibrary - false - v120 - true - Unicode - - - - - - - - - - - - - $(ProjectName)d - - - - Level3 - Disabled - false - _MBCS;GUID_WINDOWS;%(PreprocessorDefinitions) - - - true - - - - - Level3 - MaxSpeed - true - true - true - _MBCS;GUID_WINDOWS;%(PreprocessorDefinitions) - - - true - true - true - - - - - - \ No newline at end of file diff --git a/VisualStudio/CrossGuid.vcxproj.filters b/VisualStudio/CrossGuid.vcxproj.filters deleted file mode 100755 index dab9921..0000000 --- a/VisualStudio/CrossGuid.vcxproj.filters +++ /dev/null @@ -1,27 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Source Files - - - - - Header Files - - - \ No newline at end of file diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..0f6ca2d --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,10 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.externalNativeBuild +.idea diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml deleted file mode 100644 index d530671..0000000 --- a/android/AndroidManifest.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - diff --git a/android/app/.gitignore b/android/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/android/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/android/app/CMakeLists.txt b/android/app/CMakeLists.txt new file mode 100644 index 0000000..11da347 --- /dev/null +++ b/android/app/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.4.1) + +set(LIB_NAME crossguidtest) +set(XG_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) +set(XG_TEST_DIR ${XG_DIR}/test) + +add_library(${LIB_NAME} SHARED src/main/cpp/jnitest.cpp ${XG_TEST_DIR}/Test.cpp) + +target_include_directories(${LIB_NAME} PRIVATE + ${XG_DIR} + ${XG_TEST_DIR}) + +target_compile_definitions(${LIB_NAME} PRIVATE GUID_ANDROID) + +set(XG_TESTS OFF CACHE BOOL "disable tests") +add_subdirectory(${XG_DIR} ${XG_DIR}/cmake_build) + +target_link_libraries(${LIB_NAME} xg) \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle new file mode 100644 index 0000000..e715d3a --- /dev/null +++ b/android/app/build.gradle @@ -0,0 +1,39 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 25 + buildToolsVersion "26.0.0" + defaultConfig { + applicationId "ca.graemehill.crossguid.testapp" + minSdkVersion 14 + targetSdkVersion 25 + versionCode 1 + versionName "1.0" + externalNativeBuild { + cmake { + cppFlags "-std=c++11 -frtti -fexceptions" + arguments "-DANDROID_TOOLCHAIN=clang" + } + } + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + externalNativeBuild { + cmake { + path "CMakeLists.txt" + } + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + androidTestImplementation('com.android.support.test.espresso:espresso-core:3.0.0', { + exclude group: 'com.android.support', module: 'support-annotations' + }) + implementation 'com.android.support:appcompat-v7:25.4.0' + testImplementation 'junit:junit:4.12' +} diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro new file mode 100644 index 0000000..98587be --- /dev/null +++ b/android/app/proguard-rules.pro @@ -0,0 +1,25 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /usr/local/Cellar/android-sdk/24.4.1_1/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..c1a1e86 --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + diff --git a/android/app/src/main/cpp/jnitest.cpp b/android/app/src/main/cpp/jnitest.cpp new file mode 100644 index 0000000..abb1154 --- /dev/null +++ b/android/app/src/main/cpp/jnitest.cpp @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include + +#include "Test.hpp" + +JavaVM *&javaVM() { + static JavaVM *jvm; + return jvm; +} + +extern "C" +{ + +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void * /* reserved */) { + javaVM() = jvm; + return JNI_VERSION_1_6; +} + +JNIEXPORT jstring JNICALL +Java_ca_graemehill_crossguid_testapp_MainActivity_test( + JNIEnv *env, jobject /*thiz*/) +{ + std::stringstream resultStream; + xg::initJni(env); + test(resultStream); + return env->NewStringUTF(resultStream.str().c_str()); +} + +JNIEXPORT jstring JNICALL +Java_ca_graemehill_crossguid_testapp_MainActivity_newGuid( + JNIEnv *env, jobject /*thiz*/) { + return env->NewStringUTF(xg::newGuid(env).str().c_str()); +} + +JNIEXPORT jstring JNICALL +Java_ca_graemehill_crossguid_testapp_MainActivity_createGuidFromNativeThread( + JNIEnv *env, jobject /*thiz*/) { + + // there is no promise<> in armeabi of ndk + // so ugly atomic_bool wait solution + std::atomic_bool ready { false }; + std::string guid; + + std::thread([&ready, &guid](){ + JNIEnv *threadEnv; + javaVM()->AttachCurrentThread(&threadEnv, NULL); + guid = xg::newGuid(threadEnv); + javaVM()->DetachCurrentThread(); + + ready = true; + }).detach(); + + while (!ready); + return env->NewStringUTF(guid.c_str()); +} + +} diff --git a/android/app/src/main/java/ca/graemehill/crossguid/testapp/MainActivity.java b/android/app/src/main/java/ca/graemehill/crossguid/testapp/MainActivity.java new file mode 100644 index 0000000..4a285a2 --- /dev/null +++ b/android/app/src/main/java/ca/graemehill/crossguid/testapp/MainActivity.java @@ -0,0 +1,64 @@ +package ca.graemehill.crossguid.testapp; + +import android.app.Activity; +import android.os.Bundle; +import android.widget.TextView; + +import java.util.concurrent.CountDownLatch; + +public class MainActivity extends Activity { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + + final TextView textView = (TextView)findViewById(R.id.mainTextView); + textView.setText(test()); + + final TextView javaThreadView = (TextView)findViewById(R.id.javaThreadView); + javaThreadView.setText(createGuidFromJavaThread()); + + final TextView nativeThreadView = (TextView)findViewById(R.id.nativeThreadView); + nativeThreadView.setText(createGuidFromNativeThread()); + } + + public native String test(); + + private static class StringCapture { + private String value; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + + public String createGuidFromJavaThread() { + final CountDownLatch created = new CountDownLatch(1); + final StringCapture result = new StringCapture(); + new Thread(new Runnable() { + @Override + public void run() { + result.setValue(newGuid()); + created.countDown(); + } + }).start(); + try { + created.await(); + } catch (InterruptedException e) { + return "Could not get value: " + e.getMessage(); + } + return result.getValue(); + } + + public native String newGuid(); + + public native String createGuidFromNativeThread(); + static { + System.loadLibrary("crossguidtest"); + } +} diff --git a/android/app/src/main/res/layout/main.xml b/android/app/src/main/res/layout/main.xml new file mode 100644 index 0000000..8ba850a --- /dev/null +++ b/android/app/src/main/res/layout/main.xml @@ -0,0 +1,35 @@ + + + + + + + + + + diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..5507303 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..8fab6a3 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..6bc7fcd Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..1eecc0e Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..ec87dce Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..05ca079 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..6f67f21 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..8bac0f2 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..0327e13 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..bacd3e7 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..3ab3e9c --- /dev/null +++ b/android/app/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #3F51B5 + #303F9F + #FF4081 + diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..4b0fb85 --- /dev/null +++ b/android/app/src/main/res/values/strings.xml @@ -0,0 +1,6 @@ + + TestApp + GUID created from Native Thread + GUID created from Java Thread + Test results + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..5885930 --- /dev/null +++ b/android/app/src/main/res/values/styles.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 0000000..82ff274 --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,26 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + + repositories { + maven { url 'https://maven.google.com' } + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.0.0-alpha3' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + maven { url 'https://maven.google.com' } + jcenter() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..13372ae Binary files /dev/null and b/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..4714bbc --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Thu Aug 03 09:39:40 MSK 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-milestone-1-all.zip diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..69b4e93 --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,18 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +android.enableAapt2=false diff --git a/android/gradlew b/android/gradlew new file mode 100755 index 0000000..4a99613 --- /dev/null +++ b/android/gradlew @@ -0,0 +1,161 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" +LC_NUMERIC="en_US.UTF-8" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/android/gradlew.bat b/android/gradlew.bat new file mode 100644 index 0000000..8a0b282 --- /dev/null +++ b/android/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/android/jni/Android.mk b/android/jni/Android.mk deleted file mode 100644 index 655ffe5..0000000 --- a/android/jni/Android.mk +++ /dev/null @@ -1,11 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE := crossguidtest -LOCAL_CFLAGS := -Wall -LOCAL_SRC_FILES := ../../guid.cpp ../../test.cpp jnitest.cpp -LOCAL_CPP_FLAGS := -std=c++11 -LOCAL_CPPFLAGS := -DGUID_ANDROID -Wno-c++11-extensions - -include $(BUILD_SHARED_LIBRARY) diff --git a/android/jni/Application.mk b/android/jni/Application.mk deleted file mode 100644 index d6e11bd..0000000 --- a/android/jni/Application.mk +++ /dev/null @@ -1,4 +0,0 @@ -APP_STL := stlport_static -NDK_TOOLCHAIN_VERSION := clang -LOCAL_CPP_FLAGS := -DGUID_ANDROID -APP_ABI := all diff --git a/android/jni/jnitest.cpp b/android/jni/jnitest.cpp deleted file mode 100644 index 0fd8534..0000000 --- a/android/jni/jnitest.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include -#include -#include -#include -#include "../../test.h" -#include "../../guid.h" - -extern "C" -{ - - jstring Java_ca_graemehill_crossguid_testapp_MainActivity_test(JNIEnv *env, jobject thiz) - { - std::stringstream resultStream; - test(GuidGenerator(env), resultStream); - return env->NewStringUTF(resultStream.str().c_str()); - } - -} diff --git a/android/res/drawable-hdpi/ic_launcher.png b/android/res/drawable-hdpi/ic_launcher.png deleted file mode 100644 index 96a442e..0000000 Binary files a/android/res/drawable-hdpi/ic_launcher.png and /dev/null differ diff --git a/android/res/drawable-ldpi/ic_launcher.png b/android/res/drawable-ldpi/ic_launcher.png deleted file mode 100644 index 9923872..0000000 Binary files a/android/res/drawable-ldpi/ic_launcher.png and /dev/null differ diff --git a/android/res/drawable-mdpi/ic_launcher.png b/android/res/drawable-mdpi/ic_launcher.png deleted file mode 100644 index 359047d..0000000 Binary files a/android/res/drawable-mdpi/ic_launcher.png and /dev/null differ diff --git a/android/res/drawable-xhdpi/ic_launcher.png b/android/res/drawable-xhdpi/ic_launcher.png deleted file mode 100644 index 71c6d76..0000000 Binary files a/android/res/drawable-xhdpi/ic_launcher.png and /dev/null differ diff --git a/android/res/layout/main.xml b/android/res/layout/main.xml deleted file mode 100644 index ad54562..0000000 --- a/android/res/layout/main.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - diff --git a/android/res/values/strings.xml b/android/res/values/strings.xml deleted file mode 100644 index 0be1647..0000000 --- a/android/res/values/strings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - MainActivity - diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 0000000..e7b4def --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1 @@ +include ':app' diff --git a/android/src/ca/graemehill/crossguid/testapp/MainActivity.java b/android/src/ca/graemehill/crossguid/testapp/MainActivity.java deleted file mode 100644 index 99d1701..0000000 --- a/android/src/ca/graemehill/crossguid/testapp/MainActivity.java +++ /dev/null @@ -1,24 +0,0 @@ -package ca.graemehill.crossguid.testapp; - -import android.app.Activity; -import android.os.Bundle; -import android.widget.TextView; -import java.util.UUID; - -public class MainActivity extends Activity { - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.main); - - final TextView textView = (TextView)findViewById(R.id.mainTextView); - textView.setText(test()); - } - - public native String test(); - - static { - System.loadLibrary("crossguidtest"); - } -} diff --git a/android.sh b/android.sh index 3309159..bb26e9a 100755 --- a/android.sh +++ b/android.sh @@ -1,10 +1,10 @@ #!/usr/bin/env bash +export LC_NUMERIC="en_US.UTF-8" + pushd android -ndk-build clean || { exit 1; } -ndk-build || { exit 1; } -ant debug || { exit 1; } +./gradlew clean assembleDebug adb uninstall ca.graemehill.crossguid.testapp || { exit 1; } -adb install bin/TestApp-debug.apk || { exit 1; } +adb install app/build/outputs/apk/debug/app-debug.apk || { exit 1; } adb shell am start -n ca.graemehill.crossguid.testapp/ca.graemehill.crossguid.testapp.MainActivity popd diff --git a/clean.sh b/clean.sh deleted file mode 100755 index 2e3b48c..0000000 --- a/clean.sh +++ /dev/null @@ -1,6 +0,0 @@ -rm -f *.o -rm -f *.a -rm -f *.so -rm -f testmain -rm -f *.dll -rm -f *.DLL diff --git a/cmake/FindLibuuid.cmake b/cmake/FindLibuuid.cmake new file mode 100644 index 0000000..58fa330 --- /dev/null +++ b/cmake/FindLibuuid.cmake @@ -0,0 +1,43 @@ +find_package(PkgConfig) + +pkg_check_modules(PKG_LIBUUID QUIET uuid) + +set(LIBUUID_DEFINITIONS ${PKG_LIBUUID_CFLAGS_OTHER}) +set(LIBUUID_VERSION ${PKG_LIBUUID_VERSION}) + +find_path(LIBUUID_INCLUDE_DIR + NAMES uuid/uuid.h + HINTS ${PKG_LIBUUID_INCLUDE_DIRS} +) +find_library(LIBUUID_LIBRARY + NAMES uuid + HINTS ${PKG_LIBUUID_LIBRARY_DIRS} +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LibUUID + FOUND_VAR + LIBUUID_FOUND + REQUIRED_VARS + LIBUUID_LIBRARY + LIBUUID_INCLUDE_DIR + VERSION_VAR + LIBUUID_VERSION +) + +if(LIBUUID_FOUND AND NOT TARGET LibUUID::UUID) + add_library(LibUUID::UUID UNKNOWN IMPORTED) + set_target_properties(LibUUID::UUID PROPERTIES + IMPORTED_LOCATION "${LIBUUID_LIBRARY}" + INTERFACE_COMPILE_OPTIONS "${LIBUUID_DEFINITIONS}" + INTERFACE_INCLUDE_DIRECTORIES "${LIBUUID_INCLUDE_DIR}" + ) +endif() + +mark_as_advanced(LIBUUID_INCLUDE_DIR LIBUUID_LIBRARY) + +include(FeatureSummary) +set_package_properties(LIBUUID PROPERTIES + URL "http://www.kernel.org/pub/linux/utils/util-linux/" + DESCRIPTION "uuid library in util-linux" +) diff --git a/crossguid.pc.in b/crossguid.pc.in new file mode 100644 index 0000000..efcfb11 --- /dev/null +++ b/crossguid.pc.in @@ -0,0 +1,11 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@ +libdir=@CMAKE_INSTALL_FULL_LIBDIR@ + +Name: crossguid +Description: Lightweight cross platform C++ GUID/UUID library +URL: https://github.com/graeme-hill/crossguid +Version: @PROJECT_VERSION@ +Cflags: -I${includedir} +Libs: -L${libdir} -lcrossguid diff --git a/debian/changelog b/debian/changelog index 6f5b1aa..4e3bc8d 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +crossguid (0.2.2+git20190529.1.ca1bf4b-1) UNRELEASED; urgency=low + + * New upstream snapshot. + + -- Debian Janitor Fri, 25 Nov 2022 09:56:57 -0000 + crossguid (0.0+git200150803-6) unstable; urgency=medium * QA upload. diff --git a/guid.cpp b/guid.cpp deleted file mode 100644 index b90e3cf..0000000 --- a/guid.cpp +++ /dev/null @@ -1,278 +0,0 @@ -/* -The MIT License (MIT) - -Copyright (c) 2014 Graeme Hill (http://graemehill.ca) - -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 "guid.h" - -#ifdef GUID_LIBUUID -#include -#endif - -#ifdef GUID_CFUUID -#include -#endif - -#ifdef GUID_WINDOWS -#include -#endif - -#ifdef GUID_ANDROID -#include -#endif - -using namespace std; - -// overload << so that it's easy to convert to a string -ostream &operator<<(ostream &s, const Guid &guid) -{ - return s << hex << setfill('0') - << setw(2) << (int)guid._bytes[0] - << setw(2) << (int)guid._bytes[1] - << setw(2) << (int)guid._bytes[2] - << setw(2) << (int)guid._bytes[3] - << "-" - << setw(2) << (int)guid._bytes[4] - << setw(2) << (int)guid._bytes[5] - << "-" - << setw(2) << (int)guid._bytes[6] - << setw(2) << (int)guid._bytes[7] - << "-" - << setw(2) << (int)guid._bytes[8] - << setw(2) << (int)guid._bytes[9] - << "-" - << setw(2) << (int)guid._bytes[10] - << setw(2) << (int)guid._bytes[11] - << setw(2) << (int)guid._bytes[12] - << setw(2) << (int)guid._bytes[13] - << setw(2) << (int)guid._bytes[14] - << setw(2) << (int)guid._bytes[15]; -} - -// create a guid from vector of bytes -Guid::Guid(const vector &bytes) -{ - _bytes = bytes; -} - -// create a guid from array of bytes -Guid::Guid(const unsigned char *bytes) -{ - _bytes.assign(bytes, bytes + 16); -} - -// converts a single hex char to a number (0 - 15) -unsigned char hexDigitToChar(char ch) -{ - if (ch > 47 && ch < 58) - return ch - 48; - - if (ch > 96 && ch < 103) - return ch - 87; - - if (ch > 64 && ch < 71) - return ch - 55; - - return 0; -} - -// converts the two hexadecimal characters to an unsigned char (a byte) -unsigned char hexPairToChar(char a, char b) -{ - return hexDigitToChar(a) * 16 + hexDigitToChar(b); -} - -// create a guid from string -Guid::Guid(const string &fromString) -{ - _bytes.clear(); - - char charOne, charTwo; - bool lookingForFirstChar = true; - - for (const char &ch : fromString) - { - if (ch == '-') - continue; - - if (lookingForFirstChar) - { - charOne = ch; - lookingForFirstChar = false; - } - else - { - charTwo = ch; - auto byte = hexPairToChar(charOne, charTwo); - _bytes.push_back(byte); - lookingForFirstChar = true; - } - } - -} - -// create empty guid -Guid::Guid() -{ - _bytes = vector(16, 0); -} - -// copy constructor -Guid::Guid(const Guid &other) -{ - _bytes = other._bytes; -} - -// overload assignment operator -Guid &Guid::operator=(const Guid &other) -{ - _bytes = other._bytes; - return *this; -} - -// overload equality operator -bool Guid::operator==(const Guid &other) const -{ - return _bytes == other._bytes; -} - -// overload inequality operator -bool Guid::operator!=(const Guid &other) const -{ - return !((*this) == other); -} - -// This is the linux friendly implementation, but it could work on other -// systems that have libuuid available -#ifdef GUID_LIBUUID -Guid GuidGenerator::newGuid() -{ - uuid_t id; - uuid_generate(id); - return id; -} -#endif - -// this is the mac and ios version -#ifdef GUID_CFUUID -Guid GuidGenerator::newGuid() -{ - auto newId = CFUUIDCreate(NULL); - auto bytes = CFUUIDGetUUIDBytes(newId); - CFRelease(newId); - - const unsigned char byteArray[16] = - { - bytes.byte0, - bytes.byte1, - bytes.byte2, - bytes.byte3, - bytes.byte4, - bytes.byte5, - bytes.byte6, - bytes.byte7, - bytes.byte8, - bytes.byte9, - bytes.byte10, - bytes.byte11, - bytes.byte12, - bytes.byte13, - bytes.byte14, - bytes.byte15 - }; - return byteArray; -} -#endif - -// obviously this is the windows version -#ifdef GUID_WINDOWS -Guid GuidGenerator::newGuid() -{ - GUID newId; - CoCreateGuid(&newId); - - const unsigned char bytes[16] = - { - (newId.Data1 >> 24) & 0xFF, - (newId.Data1 >> 16) & 0xFF, - (newId.Data1 >> 8) & 0xFF, - (newId.Data1) & 0xff, - - (newId.Data2 >> 8) & 0xFF, - (newId.Data2) & 0xff, - - (newId.Data3 >> 8) & 0xFF, - (newId.Data3) & 0xFF, - - newId.Data4[0], - newId.Data4[1], - newId.Data4[2], - newId.Data4[3], - newId.Data4[4], - newId.Data4[5], - newId.Data4[6], - newId.Data4[7] - }; - - return bytes; -} -#endif - -// android version that uses a call to a java api -#ifdef GUID_ANDROID -GuidGenerator::GuidGenerator(JNIEnv *env) -{ - _env = env; - _uuidClass = env->FindClass("java/util/UUID"); - _newGuidMethod = env->GetStaticMethodID(_uuidClass, "randomUUID", "()Ljava/util/UUID;"); - _mostSignificantBitsMethod = env->GetMethodID(_uuidClass, "getMostSignificantBits", "()J"); - _leastSignificantBitsMethod = env->GetMethodID(_uuidClass, "getLeastSignificantBits", "()J"); -} - -Guid GuidGenerator::newGuid() -{ - jobject javaUuid = _env->CallStaticObjectMethod(_uuidClass, _newGuidMethod); - jlong mostSignificant = _env->CallLongMethod(javaUuid, _mostSignificantBitsMethod); - jlong leastSignificant = _env->CallLongMethod(javaUuid, _leastSignificantBitsMethod); - - unsigned char bytes[16] = - { - (mostSignificant >> 56) & 0xFF, - (mostSignificant >> 48) & 0xFF, - (mostSignificant >> 40) & 0xFF, - (mostSignificant >> 32) & 0xFF, - (mostSignificant >> 24) & 0xFF, - (mostSignificant >> 16) & 0xFF, - (mostSignificant >> 8) & 0xFF, - (mostSignificant) & 0xFF, - (leastSignificant >> 56) & 0xFF, - (leastSignificant >> 48) & 0xFF, - (leastSignificant >> 40) & 0xFF, - (leastSignificant >> 32) & 0xFF, - (leastSignificant >> 24) & 0xFF, - (leastSignificant >> 16) & 0xFF, - (leastSignificant >> 8) & 0xFF, - (leastSignificant) & 0xFF, - }; - return bytes; -} -#endif diff --git a/guid.h b/guid.h deleted file mode 100644 index d066577..0000000 --- a/guid.h +++ /dev/null @@ -1,102 +0,0 @@ -/* -The MIT License (MIT) - -Copyright (c) 2014 Graeme Hill (http://graemehill.ca) - -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. -*/ - -#pragma once - -#include -#include -#include -#include -#include - -#ifdef GUID_ANDROID -#include -#endif - -// Class to represent a GUID/UUID. Each instance acts as a wrapper around a -// 16 byte value that can be passed around by value. It also supports -// conversion to string (via the stream operator <<) and conversion from a -// string via constructor. -class Guid -{ - public: - - // create a guid from vector of bytes - Guid(const std::vector &bytes); - - // create a guid from array of bytes - Guid(const unsigned char *bytes); - - // create a guid from string - Guid(const std::string &fromString); - - // create empty guid - Guid(); - - // copy constructor - Guid(const Guid &other); - - // overload assignment operator - Guid &operator=(const Guid &other); - - // overload equality and inequality operator - bool operator==(const Guid &other) const; - bool operator!=(const Guid &other) const; - - private: - - // actual data - std::vector _bytes; - - // make the << operator a friend so it can access _bytes - friend std::ostream &operator<<(std::ostream &s, const Guid &guid); -}; - -// Class that can create new guids. The only reason this exists instead of -// just a global "newGuid" function is because some platforms will require -// that there is some attached context. In the case of android, we need to -// know what JNIEnv is being used to call back to Java, but the newGuid() -// function would no longer be cross-platform if we parameterized the android -// version. Instead, construction of the GuidGenerator may be different on -// each platform, but the use of newGuid is uniform. -class GuidGenerator -{ - public: -#ifdef GUID_ANDROID - GuidGenerator(JNIEnv *env); -#else - GuidGenerator() { } -#endif - - Guid newGuid(); - -#ifdef GUID_ANDROID - private: - JNIEnv *_env; - jclass _uuidClass; - jmethodID _newGuidMethod; - jmethodID _mostSignificantBitsMethod; - jmethodID _leastSignificantBitsMethod; -#endif -}; diff --git a/include/crossguid/guid.hpp b/include/crossguid/guid.hpp new file mode 100644 index 0000000..61e0f17 --- /dev/null +++ b/include/crossguid/guid.hpp @@ -0,0 +1,149 @@ +/* +The MIT License (MIT) + +Copyright (c) 2014 Graeme Hill (http://graemehill.ca) + +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. +*/ + +#pragma once + +#ifdef GUID_ANDROID +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#define BEGIN_XG_NAMESPACE namespace xg { +#define END_XG_NAMESPACE } + +BEGIN_XG_NAMESPACE + +// Class to represent a GUID/UUID. Each instance acts as a wrapper around a +// 16 byte value that can be passed around by value. It also supports +// conversion to string (via the stream operator <<) and conversion from a +// string via constructor. +class Guid +{ +public: + explicit Guid(const std::array &bytes); + explicit Guid(std::array &&bytes); + + explicit Guid(std::string_view fromString); + Guid(); + + Guid(const Guid &other) = default; + Guid &operator=(const Guid &other) = default; + Guid(Guid &&other) = default; + Guid &operator=(Guid &&other) = default; + + bool operator==(const Guid &other) const; + bool operator!=(const Guid &other) const; + + std::string str() const; + operator std::string() const; + const std::array& bytes() const; + void swap(Guid &other); + bool isValid() const; + +private: + void zeroify(); + + // actual data + std::array _bytes; + + // make the << operator a friend so it can access _bytes + friend std::ostream &operator<<(std::ostream &s, const Guid &guid); + friend bool operator<(const Guid &lhs, const Guid &rhs); +}; + +Guid newGuid(); + +#ifdef GUID_ANDROID +struct AndroidGuidInfo +{ + static AndroidGuidInfo fromJniEnv(JNIEnv *env); + + JNIEnv *env; + jclass uuidClass; + jmethodID newGuidMethod; + jmethodID mostSignificantBitsMethod; + jmethodID leastSignificantBitsMethod; + std::thread::id initThreadId; +}; + +extern AndroidGuidInfo androidInfo; + +void initJni(JNIEnv *env); + +// overloading for multi-threaded calls +Guid newGuid(JNIEnv *env); +#endif + +namespace details +{ + template struct hash; + + template + struct hash : public std::hash + { + using std::hash::hash; + }; + + + template + struct hash + { + inline std::size_t operator()(const T& v, const Rest&... rest) { + std::size_t seed = hash{}(rest...); + seed ^= hash{}(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + return seed; + } + }; +} + +END_XG_NAMESPACE + +namespace std +{ + // Template specialization for std::swap() -- + // See guid.cpp for the function definition + template <> + void swap(xg::Guid &guid0, xg::Guid &guid1) noexcept; + + // Specialization for std::hash -- this implementation + // uses std::hash on the stringification of the guid + // to calculate the hash + template <> + struct hash + { + std::size_t operator()(xg::Guid const &guid) const + { + const uint64_t* p = reinterpret_cast(guid.bytes().data()); + return xg::details::hash{}(p[0], p[1]); + } + }; +} diff --git a/linux.sh b/linux.sh deleted file mode 100755 index 1674ca8..0000000 --- a/linux.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash - -./clean.sh - -g++ -c guid.cpp -o guid.o -Wall -std=c++11 -DGUID_LIBUUID -g++ -c test.cpp -o test.o -Wall -std=c++11 -g++ -c testmain.cpp -o testmain.o -Wall -g++ test.o guid.o testmain.o -o test -luuid -chmod +x test -./test diff --git a/mac.sh b/mac.sh deleted file mode 100755 index ad9d335..0000000 --- a/mac.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash - -./clean.sh - -clang++ -c guid.cpp -o guid.o -Wall -std=c++11 -DGUID_CFUUID -clang++ -c test.cpp -o test.o -Wall -std=c++11 -clang++ -c testmain.cpp -o testmain.o -Wall -std=c++11 -clang++ test.o guid.o testmain.o -o test -framework CoreFoundation -chmod +x test -./test diff --git a/src/guid.cpp b/src/guid.cpp new file mode 100644 index 0000000..3061a9b --- /dev/null +++ b/src/guid.cpp @@ -0,0 +1,403 @@ +/* +The MIT License (MIT) + +Copyright (c) 2014 Graeme Hill (http://graemehill.ca) + +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 +#include "crossguid/guid.hpp" + +#ifdef GUID_LIBUUID +#include +#endif + +#ifdef GUID_CFUUID +#include +#endif + +#ifdef GUID_WINDOWS +#include +#endif + +#ifdef GUID_ANDROID +#include +#include +#endif + +BEGIN_XG_NAMESPACE + +#ifdef GUID_ANDROID +AndroidGuidInfo androidInfo; + +AndroidGuidInfo AndroidGuidInfo::fromJniEnv(JNIEnv *env) +{ + AndroidGuidInfo info; + info.env = env; + auto localUuidClass = env->FindClass("java/util/UUID"); + info.uuidClass = (jclass)env->NewGlobalRef(localUuidClass); + env->DeleteLocalRef(localUuidClass); + info.newGuidMethod = env->GetStaticMethodID( + info.uuidClass, "randomUUID", "()Ljava/util/UUID;"); + info.mostSignificantBitsMethod = env->GetMethodID( + info.uuidClass, "getMostSignificantBits", "()J"); + info.leastSignificantBitsMethod = env->GetMethodID( + info.uuidClass, "getLeastSignificantBits", "()J"); + info.initThreadId = std::this_thread::get_id(); + return info; +} + +void initJni(JNIEnv *env) +{ + androidInfo = AndroidGuidInfo::fromJniEnv(env); +} +#endif + +// overload << so that it's easy to convert to a string +std::ostream &operator<<(std::ostream &s, const Guid &guid) +{ + std::ios_base::fmtflags f(s.flags()); // politely don't leave the ostream in hex mode + s << std::hex << std::setfill('0') + << std::setw(2) << (int)guid._bytes[0] + << std::setw(2) << (int)guid._bytes[1] + << std::setw(2) << (int)guid._bytes[2] + << std::setw(2) << (int)guid._bytes[3] + << "-" + << std::setw(2) << (int)guid._bytes[4] + << std::setw(2) << (int)guid._bytes[5] + << "-" + << std::setw(2) << (int)guid._bytes[6] + << std::setw(2) << (int)guid._bytes[7] + << "-" + << std::setw(2) << (int)guid._bytes[8] + << std::setw(2) << (int)guid._bytes[9] + << "-" + << std::setw(2) << (int)guid._bytes[10] + << std::setw(2) << (int)guid._bytes[11] + << std::setw(2) << (int)guid._bytes[12] + << std::setw(2) << (int)guid._bytes[13] + << std::setw(2) << (int)guid._bytes[14] + << std::setw(2) << (int)guid._bytes[15]; + s.flags(f); + return s; +} + +bool operator<(const xg::Guid &lhs, const xg::Guid &rhs) +{ + return lhs.bytes() < rhs.bytes(); +} + +bool Guid::isValid() const +{ + xg::Guid empty; + return *this != empty; +} + +// convert to string using std::snprintf() and std::string +std::string Guid::str() const +{ + char one[10], two[6], three[6], four[6], five[14]; + + snprintf(one, 10, "%02x%02x%02x%02x", + _bytes[0], _bytes[1], _bytes[2], _bytes[3]); + snprintf(two, 6, "%02x%02x", + _bytes[4], _bytes[5]); + snprintf(three, 6, "%02x%02x", + _bytes[6], _bytes[7]); + snprintf(four, 6, "%02x%02x", + _bytes[8], _bytes[9]); + snprintf(five, 14, "%02x%02x%02x%02x%02x%02x", + _bytes[10], _bytes[11], _bytes[12], _bytes[13], _bytes[14], _bytes[15]); + const std::string sep("-"); + std::string out(one); + + out += sep + two; + out += sep + three; + out += sep + four; + out += sep + five; + + return out; +} + +// conversion operator for std::string +Guid::operator std::string() const +{ + return str(); +} + +// Access underlying bytes +const std::array& Guid::bytes() const +{ + return _bytes; +} + +// create a guid from vector of bytes +Guid::Guid(const std::array &bytes) : _bytes(bytes) +{ } + +// create a guid from vector of bytes +Guid::Guid(std::array &&bytes) : _bytes(std::move(bytes)) +{ } + +// converts a single hex char to a number (0 - 15) +unsigned char hexDigitToChar(char ch) +{ + // 0-9 + if (ch > 47 && ch < 58) + return ch - 48; + + // a-f + if (ch > 96 && ch < 103) + return ch - 87; + + // A-F + if (ch > 64 && ch < 71) + return ch - 55; + + return 0; +} + +bool isValidHexChar(char ch) +{ + // 0-9 + if (ch > 47 && ch < 58) + return true; + + // a-f + if (ch > 96 && ch < 103) + return true; + + // A-F + if (ch > 64 && ch < 71) + return true; + + return false; +} + +// converts the two hexadecimal characters to an unsigned char (a byte) +unsigned char hexPairToChar(char a, char b) +{ + return hexDigitToChar(a) * 16 + hexDigitToChar(b); +} + +// create a guid from string +Guid::Guid(std::string_view fromString) +{ + char charOne = '\0'; + char charTwo = '\0'; + bool lookingForFirstChar = true; + unsigned nextByte = 0; + + for (const char &ch : fromString) + { + if (ch == '-') + continue; + + if (nextByte >= 16 || !isValidHexChar(ch)) + { + // Invalid string so bail + zeroify(); + return; + } + + if (lookingForFirstChar) + { + charOne = ch; + lookingForFirstChar = false; + } + else + { + charTwo = ch; + auto byte = hexPairToChar(charOne, charTwo); + _bytes[nextByte++] = byte; + lookingForFirstChar = true; + } + } + + // if there were fewer than 16 bytes in the string then guid is bad + if (nextByte < 16) + { + zeroify(); + return; + } +} + +// create empty guid +Guid::Guid() : _bytes{ {0} } +{ } + +// set all bytes to zero +void Guid::zeroify() +{ + std::fill(_bytes.begin(), _bytes.end(), static_cast(0)); +} + +// overload equality operator +bool Guid::operator==(const Guid &other) const +{ + return _bytes == other._bytes; +} + +// overload inequality operator +bool Guid::operator!=(const Guid &other) const +{ + return !((*this) == other); +} + +// member swap function +void Guid::swap(Guid &other) +{ + _bytes.swap(other._bytes); +} + +// This is the linux friendly implementation, but it could work on other +// systems that have libuuid available +#ifdef GUID_LIBUUID +Guid newGuid() +{ + std::array data; + static_assert(std::is_same::value, "Wrong type!"); + uuid_generate(data.data()); + return Guid{std::move(data)}; +} +#endif + +// this is the mac and ios version +#ifdef GUID_CFUUID +Guid newGuid() +{ + auto newId = CFUUIDCreate(NULL); + auto bytes = CFUUIDGetUUIDBytes(newId); + CFRelease(newId); + + std::array byteArray = + {{ + bytes.byte0, + bytes.byte1, + bytes.byte2, + bytes.byte3, + bytes.byte4, + bytes.byte5, + bytes.byte6, + bytes.byte7, + bytes.byte8, + bytes.byte9, + bytes.byte10, + bytes.byte11, + bytes.byte12, + bytes.byte13, + bytes.byte14, + bytes.byte15 + }}; + return Guid{std::move(byteArray)}; +} +#endif + +// obviously this is the windows version +#ifdef GUID_WINDOWS +Guid newGuid() +{ + GUID newId; + CoCreateGuid(&newId); + + std::array bytes = + { + (unsigned char)((newId.Data1 >> 24) & 0xFF), + (unsigned char)((newId.Data1 >> 16) & 0xFF), + (unsigned char)((newId.Data1 >> 8) & 0xFF), + (unsigned char)((newId.Data1) & 0xff), + + (unsigned char)((newId.Data2 >> 8) & 0xFF), + (unsigned char)((newId.Data2) & 0xff), + + (unsigned char)((newId.Data3 >> 8) & 0xFF), + (unsigned char)((newId.Data3) & 0xFF), + + (unsigned char)newId.Data4[0], + (unsigned char)newId.Data4[1], + (unsigned char)newId.Data4[2], + (unsigned char)newId.Data4[3], + (unsigned char)newId.Data4[4], + (unsigned char)newId.Data4[5], + (unsigned char)newId.Data4[6], + (unsigned char)newId.Data4[7] + }; + + return Guid{std::move(bytes)}; +} +#endif + +// android version that uses a call to a java api +#ifdef GUID_ANDROID +Guid newGuid(JNIEnv *env) +{ + assert(env != androidInfo.env || std::this_thread::get_id() == androidInfo.initThreadId); + + jobject javaUuid = env->CallStaticObjectMethod( + androidInfo.uuidClass, androidInfo.newGuidMethod); + jlong mostSignificant = env->CallLongMethod(javaUuid, + androidInfo.mostSignificantBitsMethod); + jlong leastSignificant = env->CallLongMethod(javaUuid, + androidInfo.leastSignificantBitsMethod); + + std::array bytes = + { + (unsigned char)((mostSignificant >> 56) & 0xFF), + (unsigned char)((mostSignificant >> 48) & 0xFF), + (unsigned char)((mostSignificant >> 40) & 0xFF), + (unsigned char)((mostSignificant >> 32) & 0xFF), + (unsigned char)((mostSignificant >> 24) & 0xFF), + (unsigned char)((mostSignificant >> 16) & 0xFF), + (unsigned char)((mostSignificant >> 8) & 0xFF), + (unsigned char)((mostSignificant) & 0xFF), + (unsigned char)((leastSignificant >> 56) & 0xFF), + (unsigned char)((leastSignificant >> 48) & 0xFF), + (unsigned char)((leastSignificant >> 40) & 0xFF), + (unsigned char)((leastSignificant >> 32) & 0xFF), + (unsigned char)((leastSignificant >> 24) & 0xFF), + (unsigned char)((leastSignificant >> 16) & 0xFF), + (unsigned char)((leastSignificant >> 8) & 0xFF), + (unsigned char)((leastSignificant) & 0xFF) + }; + + env->DeleteLocalRef(javaUuid); + + return Guid{std::move(bytes)}; +} + +Guid newGuid() +{ + return newGuid(androidInfo.env); +} +#endif + + +END_XG_NAMESPACE + +// Specialization for std::swap() -- +// call member swap function of lhs, passing rhs +namespace std +{ + template <> + void swap(xg::Guid &lhs, xg::Guid &rhs) noexcept + { + lhs.swap(rhs); + } +} diff --git a/test/Test.cpp b/test/Test.cpp new file mode 100644 index 0000000..6fd47bc --- /dev/null +++ b/test/Test.cpp @@ -0,0 +1,184 @@ +#include "Test.hpp" + +int test(std::ostream &outStream) +{ + int failed = 0; + + /************************************************************************* + * HAPPY PATH TESTS + *************************************************************************/ + + auto r1 = xg::newGuid(); + auto r2 = xg::newGuid(); + auto r3 = xg::newGuid(); + + outStream << r1 << std::endl << r2 << std::endl << r3 << std::endl; + + xg::Guid s1("7bcd757f-5b10-4f9b-af69-1a1f226f3b3e"); + xg::Guid s2("16d1bd03-09a5-47d3-944b-5e326fd52d27"); + xg::Guid s3("fdaba646-e07e-49de-9529-4499a5580c75"); + xg::Guid s4("7bcd757f-5b10-4f9b-af69-1a1f226f3b3e"); + xg::Guid s5("7bcd757f-5b10-4f9b-af69-1a1f226f3b31"); + + if (r1 == r2 || r1 == r3 || r2 == r3) + { + outStream << "FAIL - not all random guids are different" << std::endl; + failed++; + } + + if (s1 == s2) + { + outStream << "FAIL - s1 and s2 should be different" << std::endl; + failed++; + } + + if (s1 != s4) + { + outStream << "FAIL - s1 and s4 should be equal" << std::endl; + failed++; + } + + if (s4 < s5) { + outStream << "FAIL - s5 should should less than s4" << std::endl; + failed++; + } + + std::stringstream ss1; + ss1 << s1; + if (ss1.str() != "7bcd757f-5b10-4f9b-af69-1a1f226f3b3e") + { + outStream << "FAIL - string from s1 stream is wrong" << std::endl; + outStream << "--> " << ss1.str() << std::endl; + failed++; + } + + if (s1.str() != "7bcd757f-5b10-4f9b-af69-1a1f226f3b3e") + { + outStream << "FAIL - string from s1.str() is wrong" << std::endl; + outStream << "--> " << s1.str() << std::endl; + failed++; + } + + std::stringstream ss2; + ss2 << s2; + if (ss2.str() != "16d1bd03-09a5-47d3-944b-5e326fd52d27") + { + outStream << "FAIL - string generated from s2 is wrong" << std::endl; + outStream << "--> " << ss2.str() << std::endl; + return 1; + } + + std::stringstream ss3; + ss3 << s3; + if (ss3.str() != "fdaba646-e07e-49de-9529-4499a5580c75") + { + outStream << "FAIL - string generated from s3 is wrong" << std::endl; + outStream << "--> " << ss3.str() << std::endl; + failed++; + } + + auto swap1 = xg::newGuid(); + auto swap2 = xg::newGuid(); + auto swap3 = swap1; + auto swap4 = swap2; + + if (swap1 != swap3 || swap2 != swap4 || swap1 == swap2) + { + outStream << "FAIL - swap guids have bad initial state" << std::endl; + failed++; + } + + swap1.swap(swap2); + + if (swap1 != swap4 || swap2 != swap3 || swap1 == swap2) + { + outStream << "FAIL - swap didn't swap" << std::endl; + failed++; + } + + { + std::unordered_map m = {{s1, 1}, {s2, 2}}; + auto it1 = m.find(s1); + auto it2 = m.find(s2); + if(!( it1 != m.end() && it1->first == s1 && it1->second == 1 && it2 != m.end() && it2->first == s2 && it2->second == 2)) + { + outStream << "FAIL - map/hash failed!" << std::endl; + failed++; + } + auto it3 = m.find(s3); + if(it3 != m.end()) + { + outStream << "FAIL - map/hash failed!" << std::endl; + failed++; + } + } + std::array bytes = + {{ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xdd + }}; + xg::Guid guidFromBytes(bytes); + xg::Guid guidFromString("0102030405060708090a0b0c0d0e0fdd"); + if (guidFromBytes != guidFromString) + { + outStream << "FAIL - String/bytes make different guids" << std::endl; + failed++; + } + + if(!std::equal(guidFromBytes.bytes().begin(), guidFromBytes.bytes().end(), bytes.begin())) + { + outStream << "FAIL - array returned from bytes() is wrong" << std::endl; + failed++; + } + + /************************************************************************* + * ERROR HANDLING + *************************************************************************/ + + xg::Guid empty; + xg::Guid twoTooFew("7bcd757f-5b10-4f9b-af69-1a1f226f3b"); + if (twoTooFew != empty || twoTooFew.isValid()) + { + outStream << "FAIL - Guid from two too few chars" << std::endl; + failed++; + } + + xg::Guid oneTooFew("16d1bd03-09a5-47d3-944b-5e326fd52d2"); + if (oneTooFew != empty || oneTooFew.isValid()) + { + outStream << "FAIL - Guid from one too few chars" << std::endl; + failed++; + } + + xg::Guid twoTooMany("7bcd757f-5b10-4f9b-af69-1a1f226f3beeff"); + if (twoTooMany != empty || twoTooMany.isValid()) + { + outStream << "FAIL - Guid from two too many chars" << std::endl; + failed++; + } + + xg::Guid oneTooMany("16d1bd03-09a5-47d3-944b-5e326fd52d27a"); + if (oneTooMany != empty || oneTooMany.isValid()) + { + outStream << "FAIL - Guid from one too many chars" << std::endl; + failed++; + } + + xg::Guid badString("!!bad-guid-string!!"); + if (badString != empty || badString.isValid()) + { + outStream << "FAIL - Guid from bad string" << std::endl; + failed++; + } + + if (failed == 0) + { + outStream << "All tests passed!" << std::endl; + return 0; + } + else + { + outStream << failed << " tests failed." << std::endl; + return 1; + } +} diff --git a/test/Test.hpp b/test/Test.hpp new file mode 100644 index 0000000..aa8b411 --- /dev/null +++ b/test/Test.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include +#include +#include + +int test(std::ostream &outStream); diff --git a/test/TestMain.cpp b/test/TestMain.cpp new file mode 100644 index 0000000..2ef5c15 --- /dev/null +++ b/test/TestMain.cpp @@ -0,0 +1,7 @@ +#include "Test.hpp" +#include + +int main() +{ + return test(std::cout); +} diff --git a/test.cpp b/test.cpp deleted file mode 100644 index beffb6d..0000000 --- a/test.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "guid.h" - -using namespace std; - -int test(GuidGenerator generator, std::ostream &outStream) -{ - auto r1 = generator.newGuid(); - auto r2 = generator.newGuid(); - auto r3 = generator.newGuid(); - - outStream << r1 << endl << r2 << endl << r3 << endl; - - Guid s1("7bcd757f-5b10-4f9b-af69-1a1f226f3b3e"); - Guid s2("16d1bd03-09a5-47d3-944b-5e326fd52d27"); - Guid s3("fdaba646-e07e-49de-9529-4499a5580c75"); - Guid s4("7bcd757f-5b10-4f9b-af69-1a1f226f3b3e"); - - if (r1 == r2 || r1 == r3 || r2 == r3) - { - outStream << "FAIL - not all random guids are different" << endl; - return 1; - } - - if (s1 == s2) - { - outStream << "FAIL - s1 and s2 should be different" << endl; - return 1; - } - - if (s1 != s4) - { - outStream << "FAIL - s1 and s4 should be equal" << endl; - return 1; - } - - stringstream ss1; - ss1 << s1; - if (ss1.str() != "7bcd757f-5b10-4f9b-af69-1a1f226f3b3e") - { - outStream << "FAIL - string generated from s1 is wrong" << endl; - return 1; - } - - stringstream ss2; - ss2 << s2; - if (ss2.str() != "16d1bd03-09a5-47d3-944b-5e326fd52d27") - { - outStream << "FAIL - string generated from s2 is wrong" << endl; - return 1; - } - - stringstream ss3; - ss3 << s3; - if (ss3.str() != "fdaba646-e07e-49de-9529-4499a5580c75") - { - outStream << "FAIL - string generated from s3 is wrong" << endl; - return 1; - } - - outStream << "All tests passed!" << endl; - - return 0; -} diff --git a/test.h b/test.h deleted file mode 100644 index 6cd8f48..0000000 --- a/test.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include "guid.h" -#include - -int test(GuidGenerator generator, std::ostream &outStream); diff --git a/testmain.cpp b/testmain.cpp deleted file mode 100644 index 34bfcd4..0000000 --- a/testmain.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "test.h" -#include - -int main(int argc, char *argv[]) -{ - return test(GuidGenerator(), std::cout); -}