diff --git a/Bin/CMakeLists.txt b/Bin/CMakeLists.txt new file mode 100644 index 0000000..d73020f --- /dev/null +++ b/Bin/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.5) + +# Create the ReaiDiff executable +add_executable(ReaiDiff ReaiDiff.c) + +# Link against the CREAIT library +target_link_libraries(ReaiDiff + PRIVATE + reai + ${CURL_LIBRARIES} +) + +# Include directories for headers +target_include_directories(ReaiDiff + PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/../Include" + "${CMAKE_PREFIX_PATH}/include" +) + +# Set output directory +set_target_properties(ReaiDiff PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" +) + +# Install the executable +install(TARGETS ReaiDiff DESTINATION bin) \ No newline at end of file diff --git a/Bin/ReaiDiff.c b/Bin/ReaiDiff.c new file mode 100644 index 0000000..5517088 --- /dev/null +++ b/Bin/ReaiDiff.c @@ -0,0 +1,219 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// ANSI color codes for terminal output +#define RESET "\033[0m" +#define RED "\033[31m" +#define GREEN "\033[32m" +#define YELLOW "\033[33m" +#define BLUE "\033[34m" +#define MAGENTA "\033[35m" +#define CYAN "\033[36m" +#define BOLD "\033[1m" + +// Function to read a file into a Str object +bool ReadFileToStr (const char* filename, Str* str) { + char* data = NULL; + size file_size = 0; + size capacity = 0; + + if (!ReadCompleteFile (filename, &data, &file_size, &capacity)) { + fprintf (stderr, "Error: Failed to read file '%s'\n", filename); + return false; + } + + // Initialize Str with the file content + *str = StrInitFromCstr (data, file_size); + // Free the original data since StrInitFromCstr makes a copy + free (data); + + return true; +} + +// Function to print a line with proper formatting and type info +void PrintLine ( + const char* prefix, + const char* color, + u64 line_num, + const Str* content, + const char* type_info +) { + if (type_info && strlen (type_info) > 0) { + printf ( + "%s%s%3llu: %.*s%s %s[%s]%s\n", + color, + prefix, + line_num + 1, + (int)content->length, + content->data, + RESET, + BLUE, + type_info, + RESET + ); + } else { + printf ( + "%s%s%3llu: %.*s%s\n", + color, + prefix, + line_num + 1, + (int)content->length, + content->data, + RESET + ); + } +} + +// Function to print diff results with colors +void PrintDiff (const char* file1, const char* file2, const DiffLines* diff) { + printf ("%s--- %s%s\n", BOLD, file1, RESET); + printf ("%s+++ %s%s\n", BOLD, file2, RESET); + + if (diff->length == 0) { + printf ("%sFiles are identical%s\n", GREEN, RESET); + return; + } + + VecForeachPtr (diff, line, { + switch (line->type) { + case DIFF_TYPE_SAM : { + PrintLine (" ", "", line->sam.line, &line->sam.content, ""); + break; + } + + case DIFF_TYPE_ADD : { + PrintLine ("+", GREEN, line->add.line, &line->add.content, "ADDED"); + break; + } + + case DIFF_TYPE_REM : { + PrintLine ("-", RED, line->rem.line, &line->rem.content, "REMOVED"); + break; + } + + case DIFF_TYPE_MOD : { + char mod_info[64]; + snprintf ( + mod_info, + sizeof (mod_info), + "MODIFIED: was line %llu", + line->mod.old_line + 1 + ); + PrintLine ("-", RED, line->mod.old_line, &line->mod.old_content, "MODIFIED: old"); + PrintLine ("+", GREEN, line->mod.new_line, &line->mod.new_content, mod_info); + break; + } + + case DIFF_TYPE_MOV : { + // Show moved lines with yellow color and clear info + printf ( + "%s~ %3llu: %.*s%s %s[MOVED: from line %llu]%s\n", + YELLOW, + line->mov.new_line + 1, + (int)line->mov.new_content.length, + line->mov.new_content.data, + RESET, + BLUE, + line->mov.old_line + 1, + RESET + ); + break; + } + + default : { + fprintf (stderr, "Warning: Unknown diff type '%c'\n", line->type); + break; + } + } + }); +} + +// Function to print usage information +void PrintUsage (const char* program_name) { + printf ("Usage: %s \n", program_name); + printf ("\nCompare two files line by line and show differences.\n"); + printf ("\nColor codes:\n"); + printf (" %s- Red:%s Lines removed from file1\n", RED, RESET); + printf (" %s+ Green:%s Lines added in file2\n", GREEN, RESET); + printf (" %s~ Yellow:%s Lines moved between files\n", YELLOW, RESET); + printf (" Normal: Lines that are the same\n"); + printf ("\nModifications are shown as a removal followed by an addition.\n"); +} + +int main (int argc, char* argv[]) { + // Check command line arguments + if (argc != 3) { + PrintUsage (argv[0]); + return 1; + } + + const char* file1 = argv[1]; + const char* file2 = argv[2]; + + // Initialize strings for file contents + Str str1, str2; + bool success = true; + + // Read first file + if (!ReadFileToStr (file1, &str1)) { + return 1; + } + + // Read second file + if (!ReadFileToStr (file2, &str2)) { + StrDeinit (&str1); + return 1; + } + + // Get the diff + DiffLines diff = GetDiff (&str1, &str2); + + // Print the diff results + PrintDiff (file1, file2, &diff); + + // Count different types of changes for summary + size same_count = 0, add_count = 0, rem_count = 0, mod_count = 0, mov_count = 0; + VecForeachPtr (&diff, line, { + switch (line->type) { + case DIFF_TYPE_SAM : + same_count++; + break; + case DIFF_TYPE_ADD : + add_count++; + break; + case DIFF_TYPE_REM : + rem_count++; + break; + case DIFF_TYPE_MOD : + mod_count++; + break; + case DIFF_TYPE_MOV : + mov_count++; + break; + } + }); + + // Print summary + printf ("\n%sSummary:%s\n", BOLD, RESET); + printf (" Same lines: %zu\n", same_count); + printf (" Added lines: %s%zu%s\n", GREEN, add_count, RESET); + printf (" Removed lines: %s%zu%s\n", RED, rem_count, RESET); + printf (" Modified lines: %s%zu%s\n", YELLOW, mod_count, RESET); + printf (" Moved lines: %s%zu%s\n", CYAN, mov_count, RESET); + printf (" Total changes: %zu\n", add_count + rem_count + mod_count + mov_count); + + // Cleanup + VecDeinit (&diff); + StrDeinit (&str1); + StrDeinit (&str2); + + // Return non-zero if files differ + return (add_count + rem_count + mod_count + mov_count) > 0 ? 1 : 0; +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 80a4e2b..5f7ea50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,9 @@ include_directories("${CMAKE_PREFIX_PATH}/include") # Add library source add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/Source/Reai") +# Add binary tools +add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/Bin") + # Add installation target for include files install(DIRECTORY Include/Reai DESTINATION include) diff --git a/Include/Reai/Api.h b/Include/Reai/Api.h index e2e1d84..ab24f47 100644 --- a/Include/Reai/Api.h +++ b/Include/Reai/Api.h @@ -492,6 +492,162 @@ extern "C" { Str* file_path ); + /// + /// GET: /v2/functions/{function_id}/decompilation/comments + /// + /// Get list of all comments created for function with given Id. + /// + /// conn[in] : Connection object with information to make secure connection with RevEngAI servers. + /// function_id[in] : Function ID to get comments for. + /// + /// SUCCESS: A vector object containing all comments created by user. + /// FAILURE: Empty object. + /// + REAI_API Comments GetDecompilationComments (Connection* conn, FunctionId function_id); + + /// + /// POST: /v2/functions/{function_id}/decompilation/comments + /// + /// Create a new comment for decompilation for function with given function id. + /// + /// conn[in] : Connection object with information to make secure connection with RevEngAI servers. + /// function_id[in] : Function ID to get comments for. + /// comment[in] : Str object containing the comment contents. + /// start_line[in] : Start line within decompilation to add comment to. + /// end_line[in] : End line within decompilation to add comment to. + /// + /// SUCCESS: A non-zero unique value associated with newly created comment, indicating comment creation was successful. + /// FAILURE: Zero. + /// + REAI_API CommentId AddDecompilationComment ( + Connection* conn, + FunctionId function_id, + Str* comment, + u64 start_line, + u64 end_line + ); + + /// + /// PATCH: /v2/functions/{function_id}/decompilation/comments/{comment_id} + /// + /// Update an existing comment. + /// + /// conn[in] : Connection object with information to make secure connection with RevEngAI servers. + /// function_id[in] : Function ID to get comments for. + /// comment_id[in] : Comment ID for comment to be updated. + /// comment[in] : Str object containing the comment contents. + /// + /// SUCCESS: true indicating successful comment update. + /// FAILURE: false. + /// + REAI_API bool UpdateDecompilationComment ( + Connection* conn, + FunctionId function_id, + CommentId comment_id, + Str* comment + ); + + /// + /// DELETE: /v2/functions/{function_id}/decompilation/comments/{comment_id} + /// + /// Delete an existing comment. + /// + /// conn[in] : Connection object with information to make secure connection with RevEngAI servers. + /// function_id[in] : Function ID to get comments for. + /// comment_id[in] : Comment ID for comment to be updated. + /// + /// SUCCESS: true indicating successful comment deletion. + /// FAILURE: false. + /// + REAI_API bool + DeleteDecompilationComment (Connection* conn, FunctionId function_id, CommentId comment_id); + + /// + /// GET: /v2/functions/{function_id}/ai-decompilation/comments + /// + /// Get list of all comments created for function with given Id. + /// + /// conn[in] : Connection object with information to make secure connection with RevEngAI servers. + /// function_id[in] : Function ID to get comments for. + /// + /// SUCCESS: A vector object containing all comments created by user. + /// FAILURE: Empty object. + /// + REAI_API Comments GetAiDecompilationComments (Connection* conn, FunctionId function_id); + + /// + /// POST: /v2/functions/{function_id}/ai-decompilation/comments + /// + /// Create a new comment for decompilation for function with given function id. + /// + /// conn[in] : Connection object with information to make secure connection with RevEngAI servers. + /// function_id[in] : Function ID to get comments for. + /// comment[in] : Str object containing the comment contents. + /// start_line[in] : Start line within decompilation to add comment to. + /// end_line[in] : End line within decompilation to add comment to. + /// + /// SUCCESS: A non-zero unique value associated with newly created comment, indicating comment creation was successful. + /// FAILURE: Zero. + /// + REAI_API CommentId AddAiDecompilationComment ( + Connection* conn, + FunctionId function_id, + Str* comment, + u64 start_line, + u64 end_line + ); + + /// + /// PATCH: /v2/functions/{function_id}/ai-decompilation/comments/{comment_id} + /// + /// Update an existing comment. + /// + /// conn[in] : Connection object with information to make secure connection with RevEngAI servers. + /// function_id[in] : Function ID to get comments for. + /// comment_id[in] : Comment ID for comment to be updated. + /// comment[in] : Str object containing the comment contents. + /// + /// SUCCESS: true indicating successful comment update. + /// FAILURE: false. + /// + REAI_API bool UpdateAiDecompilationComment ( + Connection* conn, + FunctionId function_id, + CommentId comment_id, + Str* comment + ); + + /// + /// DELETE: /v2/functions/{function_id}/ai-decompilation/comments/{comment_id} + /// + /// Delete an existing comment. + /// + /// conn[in] : Connection object with information to make secure connection with RevEngAI servers. + /// function_id[in] : Function ID to get comments for. + /// comment_id[in] : Comment ID for comment to be updated. + /// + /// SUCCESS: true indicating successful comment deletion. + /// FAILURE: false. + /// + REAI_API bool DeleteAiDecompilationComment ( + Connection* conn, + FunctionId function_id, + CommentId comment_id + ); + + + /// + /// GET: /v2/users/me/comments + /// + /// Get all comments created by this user. + /// + /// conn[in] : Connection object with information to make secure connection with RevEngAI servers. + /// + /// SUCCESS: Comments vector filled with information about all created comments. + /// FAILURE: Empty vector object. + /// + REAI_API Comments GetComments (Connection* conn); + #ifdef __cplusplus } #endif diff --git a/Include/Reai/Api/Types.h b/Include/Reai/Api/Types.h index 06c70dd..f3b2558 100644 --- a/Include/Reai/Api/Types.h +++ b/Include/Reai/Api/Types.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/Include/Reai/Api/Types/AnnFnMatch.h b/Include/Reai/Api/Types/AnnFnMatch.h deleted file mode 100644 index 34d3d9c..0000000 --- a/Include/Reai/Api/Types/AnnFnMatch.h +++ /dev/null @@ -1,39 +0,0 @@ -/** - * @file AnnFnMatch.h - * @date 2nd August 2024 - * @author Siddharth Mishra (admin@brightprogrammer.in) - * @copyright Copyright (c) RevEngAI. All Rights Reserved. - * */ - - -#ifndef REAI_ANN_FN_MATCH_H -#define REAI_ANN_FN_MATCH_H - -#include -#include -#include -#include - -typedef struct AnnFnMatch { - f64 confidence; - BinaryId binary_id; - Str binary_name; - bool debug; - FunctionId function_id; - Str function_name; - Str sha256; - FunctionId origin_function_id; -} AnnFnMatch; - -#ifdef __cplusplus -extern "C" { -#endif - - REAI_API void AnnFnMatchDeinit (AnnFnMatch* clone); - REAI_API bool AnnFnMatchInitClone (AnnFnMatch* dst, AnnFnMatch* src); - -#ifdef __cplusplus -} -#endif - -#endif // REAI_ANN_FN_MATCH_H diff --git a/Include/Reai/Api/Types/Comment.h b/Include/Reai/Api/Types/Comment.h new file mode 100644 index 0000000..a018791 --- /dev/null +++ b/Include/Reai/Api/Types/Comment.h @@ -0,0 +1,59 @@ +/** + * @file Comment.h + * @date 11th Jun 2025 + * @author Siddharth Mishra (admin@brightprogrammer.in) + * @copyright Copyright (c) RevEngAI. All Rights Reserved. + * */ + +#ifndef COMMENT_H +#define COMMENT_H + +#include +#include + +typedef struct { + Str content; + CommentId id; + UserId user_id; + Str resource_type; + ResourceId resource_id; + struct { + u32 start_line; + u32 end_line; + } context; + Str created_at; + Str updated_at; +} Comment; + +typedef Vec (Comment) Comments; + +#ifdef __cplusplus +extern "C" { +#endif + + /// + /// Init clone of object + /// Vectors in inited in clone will create new copies of data instead of sharing ownersip + /// + /// dst[out] : Destination of cloned data + /// src[in] : Cloning source + /// + /// SUCCESS : true + /// FAILURE : Does not return + /// + REAI_API bool CommentInitClone (Comment* dst, Comment* src); + + + /// + /// Deinit object + /// Must not be called manually if inside a vector that has deinit method set. + /// + /// dst[in] : Destination of cloned data + /// + REAI_API void CommentDeinit (Comment* c); + +#ifdef __cplusplus +} +#endif + +#endif // COMMENT_H diff --git a/Include/Reai/Api/Types/Common.h b/Include/Reai/Api/Types/Common.h index 5a69d27..99b25d5 100644 --- a/Include/Reai/Api/Types/Common.h +++ b/Include/Reai/Api/Types/Common.h @@ -13,6 +13,9 @@ typedef u64 FunctionId; typedef u64 CollectionId; typedef u64 ModelId; typedef u64 TeamId; +typedef u64 CommentId; +typedef u64 UserId; +typedef u64 ResourceId; typedef Vec (u64) IdsVec; @@ -22,5 +25,8 @@ typedef IdsVec FunctionIds; typedef IdsVec CollectionIds; typedef IdsVec ModelIds; typedef IdsVec TeamIds; +typedef IdsVec CommentIds; +typedef IdsVec UserIds; +typedef IdsVec ResourceIds; #endif // REAI_API_TYPES_COMMON_H diff --git a/Include/Reai/Diff.h b/Include/Reai/Diff.h new file mode 100644 index 0000000..04b8b92 --- /dev/null +++ b/Include/Reai/Diff.h @@ -0,0 +1,92 @@ +#ifndef REAI_DIFF_H +#define REAI_DIFF_H + +#include + +typedef enum { + DIFF_TYPE_SAM = 's', + DIFF_TYPE_ADD = '+', + DIFF_TYPE_REM = '-', + DIFF_TYPE_MOD = 'a', + DIFF_TYPE_MOV = 'm' +} DiffType; + +typedef struct { + DiffType type; + union { + struct { + u64 line; + Str content; + } sam, add, rem; + struct { + u64 old_line; + u64 new_line; + Str old_content; + Str new_content; + } mov, mod; + }; +} DiffLine; + +typedef Vec (DiffLine) DiffLines; + +#ifdef __cplusplus +# define DiffLineInitSam(l, s) \ + (DiffLine {.type = DIFF_TYPE_SAM, .sam.line = (l), .sam.content = StrDup (s)}) +# define DiffLineInitAdd(l, s) \ + (DiffLine {.type = DIFF_TYPE_ADD, .add.line = (l), .add.content = StrDup (s)}) +# define DiffLineInitRem(l, s) \ + (DiffLine {.type = DIFF_TYPE_REM, .rem.line = (l), .rem.content = StrDup (s)}) +# define DiffLineInitMov(lo, ln, s) \ + (DiffLine { \ + .type = DIFF_TYPE_MOV, \ + .mov.old_line = (lo), \ + .mov.new_line = (ln), \ + .mov.old_content = StrDup (s), \ + .mov.new_content = StrDup (s) \ + }) +# define DiffLineInitMod(lo, so, ln, sn) \ + (DiffLine { \ + .type = DIFF_TYPE_MOD, \ + .mov.old_line = (lo), \ + .mov.new_line = (ln), \ + .mov.old_content = StrDup (so), \ + .mov.new_content = StrDup (sn) \ + }) +#else +# define DiffLineInitSam(l, s) \ + ((DiffLine) {.type = DIFF_TYPE_SAM, .sam.line = (l), .sam.content = StrDup (s)}) +# define DiffLineInitAdd(l, s) \ + ((DiffLine) {.type = DIFF_TYPE_ADD, .add.line = (l), .add.content = StrDup (s)}) +# define DiffLineInitRem(l, s) \ + ((DiffLine) {.type = DIFF_TYPE_REM, .rem.line = (l), .rem.content = StrDup (s)}) +# define DiffLineInitMov(lo, ln, s) \ + ((DiffLine) {.type = DIFF_TYPE_MOV, \ + .mov.old_line = (lo), \ + .mov.new_line = (ln), \ + .mov.old_content = StrDup (s), \ + .mov.new_content = StrDup (s)}) +# define DiffLineInitMod(lo, so, ln, sn) \ + ((DiffLine) {.type = DIFF_TYPE_MOD, \ + .mov.old_line = (lo), \ + .mov.new_line = (ln), \ + .mov.old_content = StrDup (so), \ + .mov.new_content = StrDup (sn)}) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + REAI_API bool DiffLineInitClone (DiffLine* dst, DiffLine* src); + REAI_API void DiffLineDeinit (DiffLine* dl); + + REAI_API u32 StrLevenshteinDistance (Str* s1, Str* s2); + REAI_API bool StrAreSimilar (Str* s1, Str* s2, u32 max_distance); + + REAI_API DiffLines GetDiff (Str* og, Str* nw); + +#ifdef __cplusplus +} +#endif + +#endif // REAI_DIFF_H diff --git a/Include/Reai/Util/Str.h b/Include/Reai/Util/Str.h index 591cccf..5976b78 100644 --- a/Include/Reai/Util/Str.h +++ b/Include/Reai/Util/Str.h @@ -121,7 +121,7 @@ extern "C" { /// RETURN : +ve or -ve depending on above or below in lexical ordering /// RETURN : 0 if both are equal /// -#define StrCmp(str, ostr) memcmp ((str)->data, (ostr)->data, MIN2 ((str)->length, (ostr)->length)) +#define StrCmp(str, ostr) strncmp ((str)->data, (ostr)->data, (ostr)->length) /// /// Compare string with another const char* @@ -395,12 +395,12 @@ extern "C" { /// #define StrClear(str) VecClear (str) -#define StrFirst(str) VecFirst (str) -#define StrLast(str) VecLast (str) -#define StrBegin(str) VecBegin (str) -#define StrEnd(str) VecEnd (str) -#define StrCharAt(str) VecAt (str, idx) -#define StrCharPtrAt(str) VecPtrAt (str, idx) +#define StrFirst(str) VecFirst (str) +#define StrLast(str) VecLast (str) +#define StrBegin(str) VecBegin (str) +#define StrEnd(str) VecEnd (str) +#define StrCharAt(str, idx) VecAt (str, idx) +#define StrCharPtrAt(str, idx) VecPtrAt (str, idx) /// Push an array of chars with given length to the back of this string. /// diff --git a/Include/Reai/Util/Vec.h b/Include/Reai/Util/Vec.h index fd01a9e..73e31d0 100644 --- a/Include/Reai/Util/Vec.h +++ b/Include/Reai/Util/Vec.h @@ -82,13 +82,14 @@ typedef struct { /// #ifdef __cplusplus # define VecInit_T(v) \ - (TYPE_OF (*v \ - ) {.length = 0, \ - .capacity = 0, \ - .copy_init = (GenericCopyInit)NULL, \ - .copy_deinit = (GenericCopyDeinit)NULL, \ - .data = NULL, \ - .alignment = 1}) + (TYPE_OF (*v) { \ + .length = 0, \ + .capacity = 0, \ + .copy_init = (GenericCopyInit)NULL, \ + .copy_deinit = (GenericCopyDeinit)NULL, \ + .data = NULL, \ + .alignment = 1 \ + }) #else # define VecInit_T(v) \ ((TYPE_OF (*v)) {.length = 0, \ @@ -137,13 +138,14 @@ typedef struct { /// #ifdef __cplusplus # define VecInitWithDeepCopy_T(v, ci, cd) \ - (TYPE_OF (*v \ - ) {.length = 0, \ - .capacity = 0, \ - .copy_init = (GenericCopyInit)(ci), \ - .copy_deinit = (GenericCopyDeinit)(cd), \ - .data = NULL, \ - .alignment = 1}) + (TYPE_OF (*v) { \ + .length = 0, \ + .capacity = 0, \ + .copy_init = (GenericCopyInit)(ci), \ + .copy_deinit = (GenericCopyDeinit)(cd), \ + .data = NULL, \ + .alignment = 1 \ + }) #else # define VecInitWithDeepCopy_T(v, ci, cd) \ ((TYPE_OF (*v)) {.length = 0, \ @@ -200,13 +202,14 @@ typedef struct { /// #ifdef __cplusplus # define VecInitAligned_T(v, aln) \ - (TYPE_OF (*v \ - ) {.length = 0, \ - .capacity = 0, \ - .copy_init = (GenericCopyInit)NULL, \ - .copy_deinit = (GenericCopyDeinit)NULL, \ - .data = NULL, \ - .alignment = (aln)}) + (TYPE_OF (*v) { \ + .length = 0, \ + .capacity = 0, \ + .copy_init = (GenericCopyInit)NULL, \ + .copy_deinit = (GenericCopyDeinit)NULL, \ + .data = NULL, \ + .alignment = (aln) \ + }) #else # define VecInitAligned_T(v, aln) \ ((TYPE_OF (*v)) {.length = 0, \ @@ -272,13 +275,14 @@ typedef struct { /// #ifdef __cplusplus # define VecInitAlignedWithDeepCopy_T(v, ci, cd, aln) \ - (TYPE_OF (*v \ - ) {.length = 0, \ - .capacity = 0, \ - .copy_init = (GenericCopyInit)(ci), \ - .copy_deinit = (GenericCopyDeinit)(cd), \ - .data = NULL, \ - .alignment = (aln)}) + (TYPE_OF (*v) { \ + .length = 0, \ + .capacity = 0, \ + .copy_init = (GenericCopyInit)(ci), \ + .copy_deinit = (GenericCopyDeinit)(cd), \ + .data = NULL, \ + .alignment = (aln) \ + }) #else # define VecInitAlignedWithDeepCopy_T(v, ci, cd, aln) \ ((TYPE_OF (*v)) {.length = 0, \ diff --git a/Source/Reai/Api.c b/Source/Reai/Api.c index dc203f8..2b93f4d 100644 --- a/Source/Reai/Api.c +++ b/Source/Reai/Api.c @@ -1167,6 +1167,431 @@ Str UploadFile (Connection* conn, Str file_path) { } } +Comments GetDecompilationComments (Connection* conn, FunctionId function_id) { + if (!conn || !function_id) { + LOG_ERROR ("Invalid argument"); + return (Comments)VecInitWithDeepCopy (NULL, CommentDeinit); + } + + Str url = StrInit(); + StrPrintf (&url, "%s/v2/functions/%llu/decompilation/comments", conn->host.data, function_id); + + Str gj = StrInit(); + + if (MakeRequest (&conn->api_key, &url, NULL, &gj, "GET")) { + StrDeinit (&url); + + StrIter j = StrIterInitFromStr (&gj); + bool status = false; + Comments comments = VecInitWithDeepCopy (NULL, CommentDeinit); + JR_OBJ (j, { + JR_BOOL_KV (j, "status", status); + if (status) { + JR_ARR_KV (j, "data", { + Comment c; + c.content = StrInit(); + c.created_at = StrInit(); + c.updated_at = StrInit(); + c.resource_type = StrInit(); + JR_OBJ (j, { + JR_STR_KV (j, "content", c.content); + JR_INT_KV (j, "id", c.id); + JR_INT_KV (j, "user_id", c.user_id); + JR_STR_KV (j, "resource_type", c.resource_type); + JR_INT_KV (j, "resource_id", c.resource_id); + JR_OBJ_KV (j, "context", { + JR_INT_KV (j, "start_line", c.context.start_line); + JR_INT_KV (j, "end_line", c.context.end_line); + }); + JR_STR_KV (j, "created_at", c.created_at); + JR_STR_KV (j, "updated_at", c.updated_at); + }); + VecPushBack (&comments, c); + }); + } + }); + + StrDeinit (&gj); + + return comments; + } + + return (Comments)VecInitWithDeepCopy (NULL, CommentDeinit); +} + +CommentId AddDecompilationComment ( + Connection* conn, + FunctionId function_id, + Str* comment, + u64 start_line, + u64 end_line +) { + if (!conn || !function_id || !comment || start_line > end_line) { + LOG_ERROR ("Invalid arguments"); + return 0; + } + + + Str url = StrInit(); + StrPrintf (&url, "%s/v2/functions/%llu/decompilation/comments", conn->host.data, function_id); + + Str sj = StrInit(); + JW_OBJ (sj, { + JW_STR_KV (sj, "content", *comment); + JW_OBJ_KV (sj, "context", { + JW_INT_KV (sj, "start_line", start_line); + JW_INT_KV (sj, "end_line", end_line); + }); + }); + + Str gj = StrInit(); + + if (MakeRequest (&conn->api_key, &url, &sj, &gj, "POST")) { + StrDeinit (&url); + StrDeinit (&sj); + + StrIter j = StrIterInitFromStr (&gj); + bool status = false; + CommentId id = 0; + JR_OBJ (j, { + JR_BOOL_KV (j, "status", status); + if (status) { + JR_OBJ_KV (j, "data", { JR_INT_KV (j, "id", id); }); + } + }); + + StrDeinit (&gj); + + return id; + } + + return false; +} + +bool UpdateDecompilationComment ( + Connection* conn, + FunctionId function_id, + CommentId comment_id, + Str* comment +) { + if (!conn || !function_id || !comment_id || !comment) { + LOG_ERROR ("Invalid arguments"); + return false; + } + + + Str url = StrInit(); + StrPrintf ( + &url, + "%s/v2/functions/%llu/decompilation/comments/%llu", + conn->host.data, + function_id, + comment_id + ); + + Str sj = StrInit(); + StrPrintf (&sj, "{\"content\":\"%s\"}", comment->data); + + Str gj = StrInit(); + + if (MakeRequest (&conn->api_key, &url, &sj, &gj, "PATCH")) { + StrDeinit (&url); + StrDeinit (&sj); + + StrIter j = StrIterInitFromStr (&gj); + bool status = false; + JR_OBJ (j, { JR_BOOL_KV (j, "status", status); }); + + StrDeinit (&gj); + + return status; + } + + return false; +} + +bool DeleteDecompilationComment (Connection* conn, FunctionId function_id, CommentId comment_id) { + if (!conn || !function_id || !comment_id) { + LOG_ERROR ("Invalid arguments"); + return false; + } + + Str url = StrInit(); + StrPrintf ( + &url, + "%s/v2/functions/%llu/decompilation/comments/%llu", + conn->host.data, + function_id, + comment_id + ); + + Str gj = StrInit(); + + if (MakeRequest (&conn->api_key, &url, NULL, &gj, "DELETE")) { + StrDeinit (&url); + + StrIter j = StrIterInitFromStr (&gj); + bool status = false; + bool data = false; + JR_OBJ (j, { + JR_BOOL_KV (j, "status", status); + if (status) { + JR_BOOL_KV (j, "data", data); + } + }); + + StrDeinit (&gj); + + return data; + } + + return false; +} + +Comments GetAiDecompilationComments (Connection* conn, FunctionId function_id) { + if (!conn || !function_id) { + LOG_ERROR ("Invalid argument"); + return (Comments)VecInitWithDeepCopy (NULL, CommentDeinit); + } + + Str url = StrInit(); + StrPrintf ( + &url, + "%s/v2/functions/%llu/ai-decompilation/comments", + conn->host.data, + function_id + ); + + Str gj = StrInit(); + + if (MakeRequest (&conn->api_key, &url, NULL, &gj, "GET")) { + StrDeinit (&url); + + StrIter j = StrIterInitFromStr (&gj); + bool status = false; + Comments comments = VecInitWithDeepCopy (NULL, CommentDeinit); + JR_OBJ (j, { + JR_BOOL_KV (j, "status", status); + if (status) { + JR_ARR_KV (j, "data", { + Comment c; + c.content = StrInit(); + c.created_at = StrInit(); + c.updated_at = StrInit(); + c.resource_type = StrInit(); + JR_OBJ (j, { + JR_STR_KV (j, "content", c.content); + JR_INT_KV (j, "id", c.id); + JR_INT_KV (j, "user_id", c.user_id); + JR_STR_KV (j, "resource_type", c.resource_type); + JR_INT_KV (j, "resource_id", c.resource_id); + JR_OBJ_KV (j, "context", { + JR_INT_KV (j, "start_line", c.context.start_line); + JR_INT_KV (j, "end_line", c.context.end_line); + }); + JR_STR_KV (j, "created_at", c.created_at); + JR_STR_KV (j, "updated_at", c.updated_at); + }); + VecPushBack (&comments, c); + }); + } + }); + + StrDeinit (&gj); + + return comments; + } + + return (Comments)VecInitWithDeepCopy (NULL, CommentDeinit); +} + +CommentId AddAiDecompilationComment ( + Connection* conn, + FunctionId function_id, + Str* comment, + u64 start_line, + u64 end_line +) { + if (!conn || !function_id || !comment || start_line > end_line) { + LOG_ERROR ("Invalid arguments"); + return 0; + } + + + Str url = StrInit(); + StrPrintf ( + &url, + "%s/v2/functions/%llu/ai-decompilation/comments", + conn->host.data, + function_id + ); + + Str sj = StrInit(); + JW_OBJ (sj, { + JW_STR_KV (sj, "content", *comment); + JW_OBJ_KV (sj, "context", { + JW_INT_KV (sj, "start_line", start_line); + JW_INT_KV (sj, "end_line", end_line); + }); + }); + + Str gj = StrInit(); + + if (MakeRequest (&conn->api_key, &url, &sj, &gj, "POST")) { + StrDeinit (&url); + StrDeinit (&sj); + + StrIter j = StrIterInitFromStr (&gj); + bool status = false; + CommentId id = 0; + JR_OBJ (j, { + JR_BOOL_KV (j, "status", status); + if (status) { + JR_OBJ_KV (j, "data", { JR_INT_KV (j, "id", id); }); + } + }); + + StrDeinit (&gj); + + return id; + } + + return false; +} + +bool UpdateAiDecompilationComment ( + Connection* conn, + FunctionId function_id, + CommentId comment_id, + Str* comment +) { + if (!conn || !function_id || !comment_id || !comment) { + LOG_ERROR ("Invalid arguments"); + return false; + } + + + Str url = StrInit(); + StrPrintf ( + &url, + "%s/v2/functions/%llu/ai-decompilation/comments/%llu", + conn->host.data, + function_id, + comment_id + ); + + Str sj = StrInit(); + StrPrintf (&sj, "{\"content\":\"%s\"}", comment->data); + + Str gj = StrInit(); + + if (MakeRequest (&conn->api_key, &url, &sj, &gj, "PATCH")) { + StrDeinit (&url); + StrDeinit (&sj); + + StrIter j = StrIterInitFromStr (&gj); + bool status = false; + JR_OBJ (j, { JR_BOOL_KV (j, "status", status); }); + + StrDeinit (&gj); + + return status; + } + + return false; +} + +bool DeleteAiDecompilationComment (Connection* conn, FunctionId function_id, CommentId comment_id) { + if (!conn || !function_id || !comment_id) { + LOG_ERROR ("Invalid arguments"); + return false; + } + + Str url = StrInit(); + StrPrintf ( + &url, + "%s/v2/functions/%llu/ai-decompilation/comments/%llu", + conn->host.data, + function_id, + comment_id + ); + + Str gj = StrInit(); + + if (MakeRequest (&conn->api_key, &url, NULL, &gj, "DELETE")) { + StrDeinit (&url); + + StrIter j = StrIterInitFromStr (&gj); + bool status = false; + bool data = false; + JR_OBJ (j, { + JR_BOOL_KV (j, "status", status); + if (status) { + JR_BOOL_KV (j, "data", data); + } + }); + + StrDeinit (&gj); + + return data; + } + + return false; +} + +REAI_API Comments GetComments (Connection* conn) { + if (!conn) { + LOG_ERROR ("Invalid argument"); + return (Comments)VecInitWithDeepCopy (NULL, CommentDeinit); + } + + Str url = StrInit(); + StrPrintf (&url, "%s/v2/users/me/comments", conn->host.data); + + Str gj = StrInit(); + + if (MakeRequest (&conn->api_key, &url, NULL, &gj, "GET")) { + StrDeinit (&url); + + StrIter j = StrIterInitFromStr (&gj); + bool status = false; + Comments comments = VecInitWithDeepCopy (NULL, CommentDeinit); + JR_OBJ (j, { + JR_BOOL_KV (j, "status", status); + if (status) { + JR_ARR_KV (j, "data", { + Comment c; + c.content = StrInit(); + c.created_at = StrInit(); + c.updated_at = StrInit(); + c.resource_type = StrInit(); + JR_OBJ (j, { + JR_STR_KV (j, "content", c.content); + JR_INT_KV (j, "id", c.id); + JR_INT_KV (j, "user_id", c.user_id); + JR_STR_KV (j, "resource_type", c.resource_type); + JR_INT_KV (j, "resource_id", c.resource_id); + JR_OBJ_KV (j, "context", { + JR_INT_KV (j, "start_line", c.context.start_line); + JR_INT_KV (j, "end_line", c.context.end_line); + }); + JR_STR_KV (j, "created_at", c.created_at); + JR_STR_KV (j, "updated_at", c.updated_at); + }); + VecPushBack (&comments, c); + }); + } + }); + + StrDeinit (&gj); + + return comments; + } + + + return (Comments)VecInitWithDeepCopy (NULL, CommentDeinit); +} + Str* UrlAddQueryStr (Str* url, const char* key, const char* value, bool* is_first) { if (!url || !key) { LOG_ERROR ("Invalid arguments."); diff --git a/Source/Reai/Api/Types/AnnFnMatch.c b/Source/Reai/Api/Types/AnnFnMatch.c deleted file mode 100644 index f5db61c..0000000 --- a/Source/Reai/Api/Types/AnnFnMatch.c +++ /dev/null @@ -1,42 +0,0 @@ -/** - * @file AnnFnMatch.c - * @date 20 August 2024 - * @author Siddharth Mishra (admin@brightprogrammer.in) - * @copyright Copyright (c) RevEngAI. All Rights Reserved. - * */ - -#include -#include - -/* libc */ -#include - -void AnnFnMatchDeinit (AnnFnMatch* afn) { - if (!afn) { - LOG_FATAL ("Invalid arguments. Aborting..."); - } - - StrDeinit (&afn->binary_name); - StrDeinit (&afn->function_name); - StrDeinit (&afn->sha256); - - memset (afn, 0, sizeof (AnnFnMatch)); -} - -AnnFnMatch* ann_fn_match_clone_init (AnnFnMatch* dst, AnnFnMatch* src) { - if (!dst || !src) { - LOG_FATAL ("Invalid arguments. Aborting..."); - } - - StrInitCopy (&dst->binary_name, &src->binary_name); - StrInitCopy (&dst->function_name, &src->function_name); - StrInitCopy (&dst->sha256, &src->sha256); - - dst->confidence = src->confidence; - dst->binary_id = src->binary_id; - dst->debug = src->debug; - dst->function_id = src->function_id; - dst->origin_function_id = src->origin_function_id; - - return dst; -} diff --git a/Source/Reai/Api/Types/Comment.c b/Source/Reai/Api/Types/Comment.c new file mode 100644 index 0000000..363e374 --- /dev/null +++ b/Source/Reai/Api/Types/Comment.c @@ -0,0 +1,35 @@ +#include +#include + +#include "Reai/Util/Str.h" + +bool CommentInitClone (Comment *dst, Comment *src) { + if (!dst || !src) { + LOG_FATAL ("Invalid comments object provided."); + } + + dst->content = StrDup (&src->content); + dst->id = src->id; + dst->user_id = src->user_id; + dst->resource_type = StrDup (&src->resource_type); + dst->resource_id = src->resource_id; + dst->context.start_line = src->context.start_line; + dst->context.end_line = src->context.end_line; + dst->created_at = StrDup (&src->created_at); + dst->updated_at = StrDup (&src->updated_at); + + return true; +} + +void CommentDeinit (Comment *c) { + if (!c) { + LOG_FATAL ("Invalid comment object provided"); + } + + StrDeinit (&c->content); + StrDeinit (&c->resource_type); + StrDeinit (&c->updated_at); + StrDeinit (&c->updated_at); + + memset (c, 0, sizeof (Comment)); +} diff --git a/Source/Reai/Diff.c b/Source/Reai/Diff.c new file mode 100644 index 0000000..cf54426 --- /dev/null +++ b/Source/Reai/Diff.c @@ -0,0 +1,308 @@ +#include +#include + +#include "Reai/Util/Str.h" +#include "Reai/Util/Vec.h" + +bool DiffLineInitClone (DiffLine* dst, DiffLine* src) { + if (!dst || !src) { + LOG_FATAL ("Invalid arguments."); + } + + dst->type = src->type; + switch (dst->type) { + case DIFF_TYPE_ADD : + case DIFF_TYPE_REM : + case DIFF_TYPE_SAM : { + dst->add.line = src->add.line; + dst->add.content = StrDup (&src->add.content); + break; + } + + case DIFF_TYPE_MOD : + case DIFF_TYPE_MOV : { + dst->mov.new_line = src->mov.new_line; + dst->mov.old_line = src->mov.old_line; + dst->mov.new_content = StrDup (&src->mov.new_content); + dst->mov.old_content = StrDup (&src->mov.old_content); + break; + } + + default : { + LOG_FATAL ("Unreachable code reached : Invalid diff line type."); + } + } + + return true; +} + +void DiffLineDeinit (DiffLine* dl) { + if (!dl) { + LOG_FATAL ("Invalid argument"); + } + + switch (dl->type) { + case DIFF_TYPE_ADD : + case DIFF_TYPE_REM : + case DIFF_TYPE_SAM : { + StrDeinit (&dl->add.content); + dl->add.line = 0; + break; + } + + case DIFF_TYPE_MOV : + case DIFF_TYPE_MOD : { + StrDeinit (&dl->mod.new_content); + StrDeinit (&dl->mod.old_content); + dl->mod.new_line = 0; + dl->mod.old_line = 0; + break; + } + + default : { + LOG_FATAL ("Unreachable code reached. Invalid diff line type."); + } + } + + dl->type = 0; +} + +u64 getDiffOldLineNumber (const DiffLine* line) { + switch (line->type) { + case DIFF_TYPE_SAM : + case DIFF_TYPE_REM : + case DIFF_TYPE_ADD : + return line->sam.line; // All use the same union field + case DIFF_TYPE_MOV : + case DIFF_TYPE_MOD : + return line->mov.old_line; + default : + LOG_FATAL ("Invalid diff line type: %c", line->type); + } +} + +u64 getDiffNewLineNumber (const DiffLine* line) { + switch (line->type) { + case DIFF_TYPE_SAM : + case DIFF_TYPE_REM : + case DIFF_TYPE_ADD : + return line->sam.line; // All use the same union field + case DIFF_TYPE_MOV : + case DIFF_TYPE_MOD : + return line->mov.new_line; + default : + LOG_FATAL ("Invalid diff line type: %c", line->type); + } +} + +int DiffLineNumberCompare (const DiffLine* a, const DiffLine* b) { + if (getDiffNewLineNumber (a) <= getDiffNewLineNumber (b)) + return -1; + else + return 1; +} + +u32 StrLevenshteinDistance (Str* s1, Str* s2) { + if (!s1 || !s2) { + LOG_FATAL ("Invalid arguments"); + } + + u64 len1 = s1->length; + u64 len2 = s2->length; + const char* str1 = s1->data; + const char* str2 = s2->data; + + // Handle empty strings + if (len1 == 0) + return len2; + if (len2 == 0) + return len1; + + // Create matrix for dynamic programming + // We only need two rows for space optimization + u32* prev_row = (u32*)malloc ((len2 + 1) * sizeof (u32)); + u32* curr_row = (u32*)malloc ((len2 + 1) * sizeof (u32)); + + if (!prev_row || !curr_row) { + if (prev_row) + free (prev_row); + if (curr_row) + free (curr_row); + LOG_FATAL ("Memory allocation failed for Levenshtein distance calculation"); + } + + // Initialize first row (distance from empty string) + for (u64 j = 0; j <= len2; j++) { + prev_row[j] = j; + } + + // Fill the matrix row by row + for (u64 i = 1; i <= len1; i++) { + curr_row[0] = i; // Distance from empty string + + for (u64 j = 1; j <= len2; j++) { + u32 cost = (str1[i - 1] == str2[j - 1]) ? 0 : 1; + + // Calculate minimum of three operations: + // 1. Deletion: curr_row[j-1] + 1 + // 2. Insertion: prev_row[j] + 1 + // 3. Substitution: prev_row[j-1] + cost + u32 deletion = curr_row[j - 1] + 1; + u32 insertion = prev_row[j] + 1; + u32 substitution = prev_row[j - 1] + cost; + + curr_row[j] = MIN2 (MIN2 (deletion, insertion), substitution); + } + + // Swap rows for next iteration + u32* temp = prev_row; + prev_row = curr_row; + curr_row = temp; + } + + u32 distance = prev_row[len2]; + + free (prev_row); + free (curr_row); + + return distance; +} + +bool StrAreSimilar (Str* s1, Str* s2, u32 max_distance) { + if (!s1 || !s2) { + LOG_FATAL ("Invalid arguments"); + } + + // Quick check: if length difference is already > max_distance, they can't be similar + u64 length_diff = + (s1->length > s2->length) ? (s1->length - s2->length) : (s2->length - s1->length); + if (length_diff > max_distance) { + return false; + } + + u32 distance = StrLevenshteinDistance (s1, s2); + return distance <= max_distance; +} + +DiffLines GetDiff (Str* og, Str* nw) { + if (!og || !nw) { + LOG_FATAL ("Invalid arguments"); + } + + DiffLines diff = VecInitWithDeepCopy (NULL, DiffLineDeinit); + + Strs og_lines = StrSplit (og, "\n"); + Strs nw_lines = StrSplit (nw, "\n"); + + // Create tracking vectors to mark which lines have been matched + Vec (bool) og_matched = VecInit(); + Vec (bool) nw_matched = VecInit(); + + // Resize vectors to match the number of lines and initialize to false + VecResize (&og_matched, og_lines.length); + VecResize (&nw_matched, nw_lines.length); + + // Phase 1: Find exact matches in the same positions + size_t min_length = og_lines.length < nw_lines.length ? og_lines.length : nw_lines.length; + + for (size_t i = 0; i < min_length; i++) { + Str* og_line = VecPtrAt (&og_lines, i); + Str* nw_line = VecPtrAt (&nw_lines, i); + + // Skip empty lines in Phase 1 - they're not meaningful for exact matching + if (og_line->length == 0 || nw_line->length == 0) { + continue; + } + + if (StrCmp (og_line, nw_line) == 0) { + DiffLine same = DiffLineInitSam (i, og_line); + VecPushBack (&diff, same); + VecAt (&og_matched, i) = true; + VecAt (&nw_matched, i) = true; + } + } + + // Phase 2: Find moves and modifications in a single pass + VecForeachPtrIdx (&og_lines, og_line, og_idx, { + if (VecAt (&og_matched, og_idx)) { + continue; + } + + // Skip empty lines in Phase 2 - they're not meaningful for moves/modifications + if (og_line->length == 0) { + continue; + } + + bool found_match = false; + VecForeachPtrIdx (&nw_lines, nw_line, nw_idx, { + if (VecAt (&nw_matched, nw_idx)) + continue; + + // Skip empty lines when looking for matches + if (nw_line->length == 0) + continue; + + // First check for exact match (move) + if (StrCmp (og_line, nw_line) == 0) { + DiffLine move = DiffLineInitMov (og_idx, nw_idx, nw_line); + VecPushBack (&diff, move); + VecAt (&og_matched, og_idx) = true; + VecAt (&nw_matched, nw_idx) = true; + found_match = true; + break; + } + }); + + // If no exact match found, look for fuzzy match (modification) + if (!found_match) { + VecForeachPtrIdx (&nw_lines, nw_line, nw_idx, { + if (VecAt (&nw_matched, nw_idx)) + continue; + + // Skip empty lines when looking for fuzzy matches + if (nw_line->length == 0) + continue; + + // Calculate max distance based on string lengths + // Use 25% of the average length, with minimum of 3 and maximum of 15 + // Be more lenient to catch indentation changes + u64 avg_length = (og_line->length + nw_line->length) / 2; + u32 max_distance = MAX2 (3, MIN2 (15, avg_length / 4)); + + if (StrAreSimilar (og_line, nw_line, max_distance)) { + DiffLine mod = DiffLineInitMod (og_idx, og_line, nw_idx, nw_line); + VecPushBack (&diff, mod); + VecAt (&og_matched, og_idx) = true; + VecAt (&nw_matched, nw_idx) = true; + break; + } + }); + } + }); + + // Phase 3: Handle remaining unmatched lines (removals and additions) + VecForeachPtrIdx (&og_lines, og_line, og_idx, { + if (!VecAt (&og_matched, og_idx)) { + DiffLine removed = DiffLineInitRem (og_idx, og_line); + VecPushBack (&diff, removed); + } + }); + + VecForeachPtrIdx (&nw_lines, nw_line, nw_idx, { + if (!VecAt (&nw_matched, nw_idx)) { + DiffLine added = DiffLineInitAdd (nw_idx, nw_line); + VecPushBack (&diff, added); + } + }); + + // Phase 4: Sort the diff results by line number for proper output order + VecSort (&diff, DiffLineNumberCompare); + + // Cleanup + VecDeinit (&og_matched); + VecDeinit (&nw_matched); + VecDeinit (&og_lines); + VecDeinit (&nw_lines); + + return diff; +} diff --git a/Source/Reai/Util/Vec.c b/Source/Reai/Util/Vec.c index 5e269e7..0071678 100644 --- a/Source/Reai/Util/Vec.c +++ b/Source/Reai/Util/Vec.c @@ -53,7 +53,6 @@ void deinit_vec (GenericVec *vec, size item_size) { free (vec->data); } - memset (vec, 0, sizeof (GenericVec)); vec->data = NULL; vec->length = vec->capacity = 0; }