diff --git a/sdk/@launchdarkly/mobile-dotnet/observability/Directory.Build.props b/sdk/@launchdarkly/mobile-dotnet/observability/Directory.Build.props index 073edaec1c..ac284c84b5 100644 --- a/sdk/@launchdarkly/mobile-dotnet/observability/Directory.Build.props +++ b/sdk/@launchdarkly/mobile-dotnet/observability/Directory.Build.props @@ -1,7 +1,7 @@ LaunchDarkly.SessionReplay - 0.5.0 + 0.5.2 false LaunchDarkly LaunchDarkly diff --git a/sdk/@launchdarkly/mobile-dotnet/observability/LDObservability.Fat.csproj b/sdk/@launchdarkly/mobile-dotnet/observability/LDObservability.Fat.csproj index 3f80489dd6..1345abfe15 100644 --- a/sdk/@launchdarkly/mobile-dotnet/observability/LDObservability.Fat.csproj +++ b/sdk/@launchdarkly/mobile-dotnet/observability/LDObservability.Fat.csproj @@ -34,10 +34,12 @@ + + diff --git a/sdk/@launchdarkly/mobile-dotnet/observability/LDObservability.csproj b/sdk/@launchdarkly/mobile-dotnet/observability/LDObservability.csproj index bd8aeb5a16..f05b3b8540 100644 --- a/sdk/@launchdarkly/mobile-dotnet/observability/LDObservability.csproj +++ b/sdk/@launchdarkly/mobile-dotnet/observability/LDObservability.csproj @@ -32,10 +32,12 @@ - + - + + + diff --git a/sdk/@launchdarkly/observability-android/lib/build.gradle.kts b/sdk/@launchdarkly/observability-android/lib/build.gradle.kts index 04dac3bf86..391873a075 100644 --- a/sdk/@launchdarkly/observability-android/lib/build.gradle.kts +++ b/sdk/@launchdarkly/observability-android/lib/build.gradle.kts @@ -92,12 +92,6 @@ android { buildConfigField("String", "OBSERVABILITY_SDK_VERSION", "\"${project.version}\"") } - externalNativeBuild { - cmake { - path = file("src/main/cpp/CMakeLists.txt") - } - } - buildTypes { release { isMinifyEnabled = false diff --git a/sdk/@launchdarkly/observability-android/lib/src/main/cpp/CMakeLists.txt b/sdk/@launchdarkly/observability-android/lib/src/main/cpp/CMakeLists.txt deleted file mode 100644 index 9832b63c9f..0000000000 --- a/sdk/@launchdarkly/observability-android/lib/src/main/cpp/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -cmake_minimum_required(VERSION 3.22.1) -project(tile_hash C) - -add_library(tile_hash SHARED - nearest_divisor.c - tile_hash.c - tile_hash_jni.c -) - -target_compile_options(tile_hash PRIVATE -O3) -target_link_options(tile_hash PRIVATE -Wl,-z,max-page-size=16384) - -find_library(jnigraphics-lib jnigraphics) - -target_link_libraries(tile_hash ${jnigraphics-lib}) diff --git a/sdk/@launchdarkly/observability-android/lib/src/main/cpp/nearest_divisor.c b/sdk/@launchdarkly/observability-android/lib/src/main/cpp/nearest_divisor.c deleted file mode 100644 index 99b876d8cf..0000000000 --- a/sdk/@launchdarkly/observability-android/lib/src/main/cpp/nearest_divisor.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "nearest_divisor.h" - -int nearest_divisor(int value, int preferred, int rangeLo, int rangeHi) { - if (value <= 0) return preferred; - if (preferred >= rangeLo && preferred <= rangeHi && - preferred > 0 && value % preferred == 0) - return preferred; - - int maxDist = rangeHi - preferred; - if (preferred - rangeLo > maxDist) maxDist = preferred - rangeLo; - if (maxDist <= 0) return preferred; - - for (int offset = 1; offset <= maxDist; offset++) { - int pos = preferred + offset; - if (pos >= rangeLo && pos <= rangeHi && pos > 0 && value % pos == 0) - return pos; - int neg = preferred - offset; - if (neg >= rangeLo && neg <= rangeHi && neg > 0 && value % neg == 0) - return neg; - } - return preferred; -} diff --git a/sdk/@launchdarkly/observability-android/lib/src/main/cpp/nearest_divisor.h b/sdk/@launchdarkly/observability-android/lib/src/main/cpp/nearest_divisor.h deleted file mode 100644 index 8f75c362d4..0000000000 --- a/sdk/@launchdarkly/observability-android/lib/src/main/cpp/nearest_divisor.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef NEAREST_DIVISOR_H -#define NEAREST_DIVISOR_H - -int nearest_divisor(int value, int preferred, int rangeLo, int rangeHi); - -#endif diff --git a/sdk/@launchdarkly/observability-android/lib/src/main/cpp/tile_hash.c b/sdk/@launchdarkly/observability-android/lib/src/main/cpp/tile_hash.c deleted file mode 100644 index 90966fb33d..0000000000 --- a/sdk/@launchdarkly/observability-android/lib/src/main/cpp/tile_hash.c +++ /dev/null @@ -1,185 +0,0 @@ -#include "tile_hash.h" -#include "nearest_divisor.h" - -#if defined(__ARM_NEON) && defined(__OPTIMIZE__) -#define USE_NEON 1 -#else -#define USE_NEON 0 -#endif - -#if USE_NEON -#include -#endif - -#define TILE_W 64 - -typedef uint64_t unaligned_u64 __attribute__((aligned(1))); -typedef uint32_t unaligned_u32 __attribute__((aligned(1))); - -TileHashResult tile_hash_w64_scalar(const unsigned char *rowPtr, - int rows, - int bytesPerRow) { - uint64_t h0 = UINT64_C(0x517cc1b727220a95); - uint64_t h1 = UINT64_C(0x6c62272e07bb0142); - uint64_t h2 = UINT64_C(0x9e3779b97f4a7c15); - uint64_t h3 = UINT64_C(0xbf58476d1ce4e5b9); - - for (int y = 0; y < rows; y++) { - const unsigned char *p = rowPtr; - for (int i = 0; i < 8; i++) { - h0 += *(const unaligned_u64 *)(p); - h1 += *(const unaligned_u64 *)(p + 8); - h2 += *(const unaligned_u64 *)(p + 16); - h3 += *(const unaligned_u64 *)(p + 24); - p += 32; - } - h0 ^= h2; h1 ^= h3; - h2 += h0; h3 += h1; - rowPtr += bytesPerRow; - } - - h0 ^= h2; h1 ^= h3; - h0 ^= h0 >> 33; h0 *= UINT64_C(0xff51afd7ed558ccd); h0 ^= h0 >> 33; - h1 ^= h1 >> 29; h1 *= UINT64_C(0xc4ceb9fe1a85ec53); h1 ^= h1 >> 29; - - TileHashResult result; - result.hashLo = (int64_t)h0; - result.hashHi = (int64_t)h1; - return result; -} - -#if USE_NEON -TileHashResult tile_hash_w64_neon(const unsigned char *rowPtr, - int rows, - int bytesPerRow) { - uint64x2_t s0 = vcombine_u64(vcreate_u64(UINT64_C(0x517cc1b727220a95)), - vcreate_u64(UINT64_C(0x6c62272e07bb0142))); - uint64x2_t s1 = vcombine_u64(vcreate_u64(UINT64_C(0x9e3779b97f4a7c15)), - vcreate_u64(UINT64_C(0xbf58476d1ce4e5b9))); - - for (int y = 0; y < rows; y++) { - const unsigned char *p = rowPtr; - for (int i = 0; i < 8; i++) { - s0 = vaddq_u64(s0, vld1q_u64((const uint64_t *)p)); - s1 = vaddq_u64(s1, vld1q_u64((const uint64_t *)(p + 16))); - p += 32; - } - s0 = veorq_u64(s0, s1); - s1 = vaddq_u64(s1, s0); - rowPtr += bytesPerRow; - } - - uint64_t h0 = vgetq_lane_u64(s0, 0); - uint64_t h1 = vgetq_lane_u64(s0, 1); - uint64_t h2 = vgetq_lane_u64(s1, 0); - uint64_t h3 = vgetq_lane_u64(s1, 1); - - h0 ^= h2; h1 ^= h3; - h0 ^= h0 >> 33; h0 *= UINT64_C(0xff51afd7ed558ccd); h0 ^= h0 >> 33; - h1 ^= h1 >> 29; h1 *= UINT64_C(0xc4ceb9fe1a85ec53); h1 ^= h1 >> 29; - - TileHashResult result; - result.hashLo = (int64_t)h0; - result.hashHi = (int64_t)h1; - return result; -} -#endif - -static inline TileHashResult tile_hash_w64(const unsigned char *rowPtr, - int rows, - int bytesPerRow) { -#if USE_NEON - return tile_hash_w64_neon(rowPtr, rows, bytesPerRow); -#else - return tile_hash_w64_scalar(rowPtr, rows, bytesPerRow); -#endif -} - -TileHashResult tile_hash(const void *data, - int startX, int startY, - int endX, int endY, - int bytesPerRow) { - const int byteWidth = (endX - startX) * 4; - const int quads = byteWidth >> 5; - const int remBytes = byteWidth & 31; - const int rem8 = remBytes >> 3; - const int tail = remBytes & 4; - - const unsigned char *rowPtr = (const unsigned char *)data - + (size_t)startY * bytesPerRow - + (size_t)startX * 4; - - uint64_t h0 = UINT64_C(0x517cc1b727220a95); - uint64_t h1 = UINT64_C(0x6c62272e07bb0142); - uint64_t h2 = UINT64_C(0x9e3779b97f4a7c15); - uint64_t h3 = UINT64_C(0xbf58476d1ce4e5b9); - - for (int y = startY; y < endY; y++) { - const unsigned char *p = rowPtr; - - for (int i = 0; i < quads; i++) { - h0 += *(const unaligned_u64 *)(p); - h1 += *(const unaligned_u64 *)(p + 8); - h2 += *(const unaligned_u64 *)(p + 16); - h3 += *(const unaligned_u64 *)(p + 24); - p += 32; - } - - if (rem8 >= 1) h0 += *(const unaligned_u64 *)(p); - if (rem8 >= 2) h1 += *(const unaligned_u64 *)(p + 8); - if (rem8 >= 3) h2 += *(const unaligned_u64 *)(p + 16); - if (tail) h3 += (uint64_t)(*(const unaligned_u32 *)(p + rem8 * 8)); - - h0 ^= h2; h1 ^= h3; - h2 += h0; h3 += h1; - - rowPtr += bytesPerRow; - } - - h0 ^= h2; h1 ^= h3; - h0 ^= h0 >> 33; h0 *= UINT64_C(0xff51afd7ed558ccd); h0 ^= h0 >> 33; - h1 ^= h1 >> 29; h1 *= UINT64_C(0xc4ceb9fe1a85ec53); h1 ^= h1 >> 29; - - TileHashResult result; - result.hashLo = (int64_t)h0; - result.hashHi = (int64_t)h1; - return result; -} - -TileLayout tile_compute_layout(int imageWidth, int imageHeight) { - TileLayout layout; - layout.tileWidth = TILE_W; - layout.tileHeight = nearest_divisor(imageHeight, 22, 22, 44); - layout.columns = (imageWidth + TILE_W - 1) / TILE_W; - layout.rows = (imageHeight + layout.tileHeight - 1) / layout.tileHeight; - return layout; -} - -void tile_compute_all(const void *data, - int imageWidth, int imageHeight, - int bytesPerRow, - TileLayout layout, - TileHashResult *out) { - const int fullCols = imageWidth / TILE_W; - int idx = 0; - - for (int row = 0; row < layout.rows; row++) { - int startY = row * layout.tileHeight; - int tileRows = layout.tileHeight; - if (startY + tileRows > imageHeight) tileRows = imageHeight - startY; - - for (int col = 0; col < fullCols; col++) { - const unsigned char *rowPtr = (const unsigned char *)data - + (size_t)startY * bytesPerRow - + (size_t)(col * TILE_W) * 4; - out[idx] = tile_hash_w64(rowPtr, tileRows, bytesPerRow); - idx++; - } - - if (fullCols < layout.columns) { - int startX = fullCols * TILE_W; - out[idx] = tile_hash(data, startX, startY, imageWidth, startY + tileRows, bytesPerRow); - idx++; - } - } -} diff --git a/sdk/@launchdarkly/observability-android/lib/src/main/cpp/tile_hash.h b/sdk/@launchdarkly/observability-android/lib/src/main/cpp/tile_hash.h deleted file mode 100644 index 60d3103f86..0000000000 --- a/sdk/@launchdarkly/observability-android/lib/src/main/cpp/tile_hash.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef TILE_HASH_H -#define TILE_HASH_H - -#include - -typedef struct { - int64_t hashLo; - int64_t hashHi; -} TileHashResult; - -typedef struct { - int rows; - int columns; - int tileWidth; - int tileHeight; -} TileLayout; - -/// Computes a fast non-cryptographic hash over the pixel rectangle -/// [startX, endX) x [startY, endY) in a 4-bytes-per-pixel bitmap. -TileHashResult tile_hash(const void *data, - int startX, int startY, - int endX, int endY, - int bytesPerRow); - -/// Always-scalar variant of tile_hash_w64, for parity testing. -TileHashResult tile_hash_w64_scalar(const unsigned char *rowPtr, - int rows, - int bytesPerRow); - -#if defined(__ARM_NEON) && defined(__OPTIMIZE__) -/// Always-NEON variant of tile_hash_w64, for parity testing. -TileHashResult tile_hash_w64_neon(const unsigned char *rowPtr, - int rows, - int bytesPerRow); -#endif - -/// Computes tile layout (tile dimensions, row/column counts) for an image. -TileLayout tile_compute_layout(int imageWidth, int imageHeight); - -/// Hashes every tile in the image and writes results to `out`. -/// `out` must have space for layout.rows * layout.columns elements. -void tile_compute_all(const void *data, - int imageWidth, int imageHeight, - int bytesPerRow, - TileLayout layout, - TileHashResult *out); - -#endif diff --git a/sdk/@launchdarkly/observability-android/lib/src/main/cpp/tile_hash_jni.c b/sdk/@launchdarkly/observability-android/lib/src/main/cpp/tile_hash_jni.c deleted file mode 100644 index 046e3b23be..0000000000 --- a/sdk/@launchdarkly/observability-android/lib/src/main/cpp/tile_hash_jni.c +++ /dev/null @@ -1,94 +0,0 @@ -#include -#include -#include -#include -#include "tile_hash.h" - -/* - * Returned LongArray layout: - * [0] rows - * [1] columns - * [2] tileWidth - * [3] tileHeight - * [4 + i*2] hashLo for tile i - * [4 + i*2+1] hashHi for tile i - */ -JNIEXPORT jlongArray JNICALL -Java_com_launchdarkly_observability_replay_capture_TileHashNative_nativeCompute( - JNIEnv *env, jclass clazz, jobject bitmap) -{ - AndroidBitmapInfo info; - if (AndroidBitmap_getInfo(env, bitmap, &info) != ANDROID_BITMAP_RESULT_SUCCESS) - return NULL; - - if (info.width == 0 || info.height == 0) - return NULL; - - if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) - return NULL; - - void *pixels; - if (AndroidBitmap_lockPixels(env, bitmap, &pixels) != ANDROID_BITMAP_RESULT_SUCCESS) - return NULL; - - TileLayout layout = tile_compute_layout((int)info.width, (int)info.height); - if (layout.rows <= 0 || layout.columns <= 0) { - AndroidBitmap_unlockPixels(env, bitmap); - return NULL; - } - - if ((size_t)layout.rows > SIZE_MAX / (size_t)layout.columns) { - AndroidBitmap_unlockPixels(env, bitmap); - return NULL; - } - size_t totalTiles = (size_t)layout.rows * (size_t)layout.columns; - - if (totalTiles > SIZE_MAX / sizeof(TileHashResult)) { - AndroidBitmap_unlockPixels(env, bitmap); - return NULL; - } - - TileHashResult *results = (TileHashResult *)malloc(totalTiles * sizeof(TileHashResult)); - if (!results) { - AndroidBitmap_unlockPixels(env, bitmap); - return NULL; - } - - tile_compute_all(pixels, - (int)info.width, (int)info.height, - (int)info.stride, - layout, results); - - AndroidBitmap_unlockPixels(env, bitmap); - - if (totalTiles > (SIZE_MAX - 4U) / 2U) { - free(results); - return NULL; - } - size_t totalLongs = 4U + totalTiles * 2U; - if (totalLongs > (size_t)INT_MAX) { - free(results); - return NULL; - } - - const jsize arrayLen = (jsize)totalLongs; - jlongArray out = (*env)->NewLongArray(env, arrayLen); - if (!out) { - free(results); - return NULL; - } - - jlong header[4] = { - layout.rows, - layout.columns, - layout.tileWidth, - layout.tileHeight, - }; - (*env)->SetLongArrayRegion(env, out, 0, 4, header); - - /* TileHashResult is {int64_t, int64_t} — same layout as jlong[2] */ - (*env)->SetLongArrayRegion(env, out, 4, (jsize)(totalTiles * 2U), (const jlong *)results); - - free(results); - return out; -} diff --git a/sdk/@launchdarkly/observability-android/lib/src/main/jniLibs/arm64-v8a/libsession_replay_c.so b/sdk/@launchdarkly/observability-android/lib/src/main/jniLibs/arm64-v8a/libsession_replay_c.so new file mode 100755 index 0000000000..f7df94a7fa Binary files /dev/null and b/sdk/@launchdarkly/observability-android/lib/src/main/jniLibs/arm64-v8a/libsession_replay_c.so differ diff --git a/sdk/@launchdarkly/observability-android/lib/src/main/jniLibs/armeabi-v7a/libsession_replay_c.so b/sdk/@launchdarkly/observability-android/lib/src/main/jniLibs/armeabi-v7a/libsession_replay_c.so new file mode 100755 index 0000000000..ce217a5593 Binary files /dev/null and b/sdk/@launchdarkly/observability-android/lib/src/main/jniLibs/armeabi-v7a/libsession_replay_c.so differ diff --git a/sdk/@launchdarkly/observability-android/lib/src/main/jniLibs/x86/libsession_replay_c.so b/sdk/@launchdarkly/observability-android/lib/src/main/jniLibs/x86/libsession_replay_c.so new file mode 100755 index 0000000000..9a6ac72097 Binary files /dev/null and b/sdk/@launchdarkly/observability-android/lib/src/main/jniLibs/x86/libsession_replay_c.so differ diff --git a/sdk/@launchdarkly/observability-android/lib/src/main/jniLibs/x86_64/libsession_replay_c.so b/sdk/@launchdarkly/observability-android/lib/src/main/jniLibs/x86_64/libsession_replay_c.so new file mode 100755 index 0000000000..5bb50b1316 Binary files /dev/null and b/sdk/@launchdarkly/observability-android/lib/src/main/jniLibs/x86_64/libsession_replay_c.so differ diff --git a/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/TileHashNative.kt b/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/TileHashNative.kt index 403d6dc7a8..6d71b5b0b6 100644 --- a/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/TileHashNative.kt +++ b/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/replay/capture/TileHashNative.kt @@ -7,7 +7,7 @@ internal object TileHashNative { init { isAvailable = try { - System.loadLibrary("tile_hash") + System.loadLibrary("session_replay_c") true } catch (_: UnsatisfiedLinkError) { false