Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update C++ code #32

Merged
merged 1 commit into from
Apr 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
*.pbxproj -text
# specific for windows script files
*.bat text eol=crlf
*.bat text eol=crlf
# iOS Headers are mainly Objective-C++
ios/**/*.h linguist-language=Objective-C++
# Android Headers are C++
android/**/*.h linguist-language=C++
9 changes: 6 additions & 3 deletions .github/workflows/build-android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,20 @@ on:
- 'example/yarn.lock'

jobs:
build_example:
build_android_example:
name: Build Android Example App
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup JDK 11
- name: Setup JDK 17
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 11
java-version: 17

- name: Setup Android SDK
uses: android-actions/setup-android@v3

- uses: actions/setup-node@v4
with:
Expand Down
9 changes: 7 additions & 2 deletions .github/workflows/build-ios.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ on:
- 'example/ios/**'

jobs:
build:
build_ios_example:
name: Build iOS Example App
runs-on: macOS-latest
defaults:
Expand Down Expand Up @@ -51,6 +51,11 @@ jobs:
bundler-cache: true
working-directory: example/ios

- name: Upgrade RubyGems
run: |
gem update --system 3.2.3
gem install bundler --no-document

- name: Restore Pods cache
uses: actions/cache@v4
with:
Expand All @@ -62,7 +67,7 @@ jobs:
restore-keys: |
${{ runner.os }}-pods-
- name: Install Pods
run: bundle exec pod check || bundle exec pod install
run: yarn pods
- name: Install xcpretty
run: gem install xcpretty
- name: Build App
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# react-native-quick-base64

A native implementation of Base64 in C++ for React Native.
4x faster than [base64-js](https://github.com/beatgammit/base64-js) on an iPhone 11 Pro.
16x faster than [base64-js](https://github.com/beatgammit/base64-js) on an iPhone 15 Pro simulator.
Try the benchmarks under [example](./example).

## Installation
Expand Down
32 changes: 23 additions & 9 deletions android/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
project(QuickBase64)
cmake_minimum_required(VERSION 3.9.0)

set (PACKAGE_NAME "react-native-quick-base64")
set (PACKAGE_NAME "quickbase64")
set (BUILD_DIR ${CMAKE_SOURCE_DIR}/build)
set (CMAKE_VERBOSE_MAKEFILE ON)
set (CMAKE_CXX_STANDARD 14)

file(TO_CMAKE_PATH "${NODE_MODULES_DIR}/react-native/ReactCommon/jsi/jsi/jsi.cpp" libPath)
set (CMAKE_CXX_STANDARD 17)

include_directories(
../cpp
Expand All @@ -14,14 +13,29 @@ include_directories(
"${NODE_MODULES_DIR}/react-native/ReactCommon/jsi"
)

add_library(quickbase64 # Library name
add_library(
${PACKAGE_NAME}
SHARED
${libPath}
../cpp/base64.cpp
../cpp/base64.h
../cpp/react-native-quick-base64.cpp
../cpp/react-native-quick-base64.h
cpp-adapter.cpp
)
)

# Configure C++ 17
set_target_properties(
${PACKAGE_NAME} PROPERTIES
CXX_STANDARD 17
CXX_EXTENSIONS OFF
POSITION_INDEPENDENT_CODE ON
)

find_package(ReactAndroid REQUIRED CONFIG)
find_library(log-lib log)

target_link_libraries(quickbase64)
target_link_libraries(
${PACKAGE_NAME}
${log-lib} # <-- Logcat logger
ReactAndroid::jsi # <-- JSI
android # <-- Android JNI core
)
196 changes: 83 additions & 113 deletions android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,154 +1,124 @@
import java.nio.file.Paths
import com.android.Version

def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION
def agpVersionMajor = agpVersion.tokenize('.')[0].toInteger()
def androidManifestPath = agpVersionMajor >= 7 ? 'src/main/AndroidManifest.xml' : 'src/hasNamespace/AndroidManifest.xml'

buildscript {
// Buildscript is evaluated before everything else so we can't use getExtOrDefault
repositories {
google()
maven {
url "https://plugins.gradle.org/m2/"
}
mavenCentral()
google()
}

dependencies {
classpath 'com.android.tools.build:gradle:4.2.2'
classpath 'com.android.tools.build:gradle:8.3.1'
}
}

def resolveBuildType() {
Gradle gradle = getGradle()
String tskReqStr = gradle.getStartParameter().getTaskRequests()['args'].toString()

return tskReqStr.contains('Release') ? 'release' : 'debug'
}

def isNewArchitectureEnabled() {
// To opt-in for the New Architecture, you can either:
// - Set `newArchEnabled` to true inside the `gradle.properties` file
// - Invoke gradle with `-newArchEnabled=true`
// - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true`
return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
}

if (isNewArchitectureEnabled()) {
apply plugin: 'com.facebook.react'
}
apply plugin: 'com.android.library'

def getExtOrDefault(name) {
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['QuickBase64_' + name]
def safeExtGet(prop, fallback) {
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}

def getExtOrIntegerDefault(name) {
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties['QuickBase64_' + name]).toInteger()
def reactNativeArchitectures() {
def value = project.getProperties().get("reactNativeArchitectures")
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
}

static def findNodeModules(baseDir) {
def basePath = baseDir.toPath().normalize()
// Node's module resolution algorithm searches up to the root directory,
// after which the base path will be null
while (basePath) {
def nodeModulesPath = Paths.get(basePath.toString(), "node_modules")
def reactNativePath = Paths.get(nodeModulesPath.toString(), "react-native")
if (nodeModulesPath.toFile().exists() && reactNativePath.toFile().exists()) {
return nodeModulesPath.toString()
repositories {
mavenCentral()
}

android {
compileSdkVersion safeExtGet("compileSdkVersion", 28)
if (agpVersionMajor >= 7) {
namespace 'com.reactnativequickbase64'
}

if (agpVersionMajor >= 8) {
buildFeatures {
buildConfig = true
}
basePath = basePath.getParent()
}
throw new GradleException("react-native-quick-base64: Failed to find node_modules/ path!")
}

def nodeModules = findNodeModules(projectDir);
logger.warn("react-native-quick-base64: node_modules/ found at: ${nodeModules}");
sourceSets {
main {
manifest.srcFile androidManifestPath
}
}

android {
compileSdkVersion getExtOrIntegerDefault('compileSdkVersion')
buildToolsVersion getExtOrDefault('buildToolsVersion')
ndkVersion getExtOrDefault('ndkVersion')
// Used to override the NDK path/version on internal CI or by allowing
// users to customize the NDK path/version from their root project (e.g. for M1 support)
if (rootProject.hasProperty("ndkPath")) {
ndkPath rootProject.ext.ndkPath
}
if (rootProject.hasProperty("ndkVersion")) {
ndkVersion rootProject.ext.ndkVersion
}

buildFeatures {
prefab true
}

defaultConfig {
minSdkVersion 21
targetSdkVersion getExtOrIntegerDefault('targetSdkVersion')
minSdkVersion safeExtGet('minSdkVersion', 21)
targetSdkVersion safeExtGet('targetSdkVersion', 28)
versionCode 1
versionName "1.0"

buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
externalNativeBuild {
cmake {
cppFlags "-O2 -frtti -fexceptions -Wall -Wno-unused-variable -fstack-protector-all"
abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
arguments "-DNODE_MODULES_DIR=${nodeModules}"
}
}

}

externalNativeBuild {
cmake {
path "CMakeLists.txt"
cppFlags "-O2 -frtti -fexceptions -Wall -Wno-unused-variable -fstack-protector-all"
arguments "-DANDROID_STL=c++_shared"
abiFilters (*reactNativeArchitectures())
}
}

buildTypes {
release {
minifyEnabled false
}
}
lintOptions {
disable 'GradleCompatible'
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}

repositories {
mavenCentral()
google()

def found = false
def defaultDir = null
def androidSourcesName = 'React Native sources'

if (rootProject.ext.has('reactNativeAndroidRoot')) {
defaultDir = rootProject.ext.get('reactNativeAndroidRoot')
} else {
defaultDir = file("$nodeModules/react-native/android")
}

if (defaultDir.exists()) {
maven {
url defaultDir.toString()
name androidSourcesName
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}

logger.info(":${project.name}:reactNativeAndroidRoot ${defaultDir.canonicalPath}")
found = true
} else {
def parentDir = rootProject.projectDir

1.upto(5, {
if (found) return true
parentDir = parentDir.parentFile

def androidSourcesDir = new File(
parentDir,
'node_modules/react-native'
)

def androidPrebuiltBinaryDir = new File(
parentDir,
'node_modules/react-native/android'
)

if (androidPrebuiltBinaryDir.exists()) {
maven {
url androidPrebuiltBinaryDir.toString()
name androidSourcesName
}

logger.info(":${project.name}:reactNativeAndroidRoot ${androidPrebuiltBinaryDir.canonicalPath}")
found = true
} else if (androidSourcesDir.exists()) {
maven {
url androidSourcesDir.toString()
name androidSourcesName
}

logger.info(":${project.name}:reactNativeAndroidRoot ${androidSourcesDir.canonicalPath}")
found = true
}
})
}

if (!found) {
throw new GradleException(
"${project.name}: unable to locate React Native android sources. " +
"Ensure you have you installed React Native as a dependency in your project and try again."
)
packagingOptions {
doNotStrip resolveBuildType() == 'debug' ? "**/**/*.so" : ''
excludes = [
"META-INF",
"META-INF/**",
"**/libjsi.so",
"**/libc++_shared.so"
]
}
}

dependencies {
// noinspection GradleDynamicVersion
api 'com.facebook.react:react-native:+'
//noinspection GradleDynamicVersion
implementation "com.facebook.react:react-android:+"
}
4 changes: 3 additions & 1 deletion android/cpp-adapter.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#include <jni.h>
#include "react-native-quick-base64.h"

using namespace facebook;

extern "C"
JNIEXPORT void JNICALL
Java_com_reactnativequickbase64_QuickBase64Module_initialize(JNIEnv* env, jclass clazz, jlong jsiPtr) {
installBase64(*reinterpret_cast<facebook::jsi::Runtime*>(jsiPtr));
installBase64(*reinterpret_cast<jsi::Runtime*>(jsiPtr));
}

extern "C"
Expand Down
1 change: 0 additions & 1 deletion android/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
QuickBase64_kotlinVersion=1.6.0
QuickBase64_compileSdkVersion=30
QuickBase64_buildToolsVersion=30
QuickBase64_targetSdkVersion=30
Binary file added android/gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
5 changes: 5 additions & 0 deletions android/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
5 changes: 5 additions & 0 deletions android/src/hasNamespace/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.reactnativequickbase64"
>

</manifest>
3 changes: 1 addition & 2 deletions android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.reactnativequickbase64">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

</manifest>
Loading
Loading