<commit_before>#ifdef _WIN32

#include "native.h"
#include "generic.h"
#include "win.h"
#include <vector>

using namespace std;

// TODO Find the right size for this
#define EVENT_BUFFER_SIZE (16*1024)

#define CREATE_SHARE (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
#define CREATE_FLAGS (FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED)
#define EVENT_MASK (FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | \
                    FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE)

class Server {
public:
    Server(
        JavaVM *jvm,
        JNIEnv *env,
        jobject watcherCallback,
        wchar_t drivePath[4],
        vector<wchar_t*>* watchedPaths
    ) {
        this->jvm = jvm;
        this->watcherCallback = env->NewGlobalRef(watcherCallback);
        this->stopEventHandle = CreateEvent(NULL, FALSE, FALSE, NULL);
        this->watchedPaths = watchedPaths;
        wcscpy_s(this->drivePath, 4, drivePath);

        this->threadHandle = CreateThread(
            NULL,                   // default security attributes
            0,                      // use default stack size
            EventProcessingThread,  // thread function name
            this,                   // argument to thread function
            0,                      // use default creation flags
            NULL                    // the thread identifier
        );
        SetThreadPriority(this->threadHandle, THREAD_PRIORITY_ABOVE_NORMAL);
    }

    void close(JNIEnv *env) {
        SetEvent(this->stopEventHandle);
        WaitForSingleObject(this->threadHandle, INFINITE);
        CloseHandle(this->threadHandle);
        CloseHandle(this->stopEventHandle);
        for (auto i = watchedPaths->begin(); i != watchedPaths->end(); i++) {
            free(*i);
        }
        delete watchedPaths;
        env->DeleteGlobalRef(this->watcherCallback);
    }

    static DWORD WINAPI EventProcessingThread(LPVOID data) {
        Server *server = (Server*) data;
        server->run();
        return 0;
    }

private:
    JavaVM *jvm;
    wchar_t drivePath[4];
    vector<wchar_t*>* watchedPaths;
    jobject watcherCallback;

    HANDLE stopEventHandle;
    HANDLE threadHandle;

    void run() {
        printf("~~~~ Starting thread\n");

        // TODO Extract this logic to some shared function
        JNIEnv *env;
        jint statAttach = jvm->AttachCurrentThreadAsDaemon((void **) &(env), NULL);
        if (statAttach != JNI_OK) {
            printf("Failed to attach JNI to current thread: %d\n", statAttach);
            return;
        }

        OVERLAPPED overlapped;
        memset(&overlapped, 0, sizeof(overlapped));
        overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

        HANDLE hDrive = CreateFileW(drivePath, GENERIC_READ, CREATE_SHARE, NULL, OPEN_EXISTING, CREATE_FLAGS, NULL);

        char buffer[EVENT_BUFFER_SIZE];
        HANDLE handles[2] = {stopEventHandle, overlapped.hEvent};
        while (true) {
            int rcDrive = ReadDirectoryChangesW(
                hDrive,
                buffer,
                sizeof(buffer),
                TRUE,
                EVENT_MASK,
                NULL,
                &overlapped,
                NULL
            );
            if (rcDrive == 0) {
                // TODO Error handling
                printf("~~~~ Couldn't read directory: %d - %d\n", rcDrive, GetLastError());
                break;
            }

            DWORD rc = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
            if (rc == WAIT_OBJECT_0) {
                break;
            }
            if (rc == WAIT_OBJECT_0 + 1) {
                DWORD dwBytesReturned;
                if (!GetOverlappedResult(hDrive, &overlapped, &dwBytesReturned, FALSE)) {
                    // TODO Error handling
                    printf("~~~~ Failed to wait\n");
                    break;
                }

                if (dwBytesReturned == 0) {
                    // don't send dirty too much, everything is changed anyway
                    // TODO Understand what this does
                    if (WaitForSingleObject(stopEventHandle, 500) == WAIT_OBJECT_0)
                        break;

                    // Got a buffer overflow => current changes lost => send INVALIDATE on root
                    reportEvent(env, FILE_EVENT_INVALIDATE, drivePath, 3);
                } else {
                    FILE_NOTIFY_INFORMATION *info = (FILE_NOTIFY_INFORMATION *)buffer;
                    do {
                        handlePathChanged(env, info);
                        info = (FILE_NOTIFY_INFORMATION *)((char *)info + info->NextEntryOffset);
                    } while (info->NextEntryOffset != 0);
                }
            }
        }

        printf("~~~~ Stopping thread\n");

        CloseHandle(overlapped.hEvent);
        CloseHandle(hDrive);

        // TODO Extract this logic to some shared function
        jint statDetach = jvm->DetachCurrentThread();
        if (statDetach != JNI_OK) {
            printf("Failed to detach JNI from current thread: %d\n", statAttach);
            return;
        }

        return;
    }

    void reportEvent(JNIEnv *env, jint type, wchar_t *changedPath, int changedPathLen) {
        jstring changedPathJava = wchar_to_java(env, changedPath, changedPathLen, NULL);
        jclass callback_class = env->GetObjectClass(watcherCallback);
        jmethodID methodCallback = env->GetMethodID(callback_class, "pathChanged", "(ILjava/lang/String;)V");
        // TODO Do we need to add a global reference to the string here?
        env->CallVoidMethod(watcherCallback, methodCallback, type, changedPathJava);
    }

    void handlePathChanged(JNIEnv *env, FILE_NOTIFY_INFORMATION *info) {
        int pathLen = info->FileNameLength / sizeof(wchar_t);
        wchar_t *changedPath = add_prefix(info->FileName, pathLen, drivePath);
        int changedPathLen = pathLen + 3;

        wprintf(L"~~~~ Changed: 0x%x %ls\n", info->Action, changedPath);

        jint type;
        if (info->Action == FILE_ACTION_ADDED || info->Action == FILE_ACTION_RENAMED_NEW_NAME) {
            type = FILE_EVENT_CREATED;
        } else if (info->Action == FILE_ACTION_REMOVED || info->Action == FILE_ACTION_RENAMED_OLD_NAME) {
            type = FILE_EVENT_REMOVED;
        } else if (info->Action == FILE_ACTION_MODIFIED) {
            type = FILE_EVENT_MODIFIED;
        } else {
            wprintf(L"~~~~ Unknown event 0x%x for %ls\n", info->Action, changedPath);
            type = FILE_EVENT_UNKNOWN;
        }

        bool watching = false;
        for (auto i = watchedPaths->begin(); i != watchedPaths->end(); i++) {
            wchar_t *watchedPath = *i;
            wprintf(L"~~~~ Checking if '%ls' starts with '%ls'\n", changedPath, watchedPath);
            if (wcsncmp(watchedPath, changedPath, wcslen(watchedPath)) == 0) {
                watching = true;
                break;
            }
        }
        if (!watching) {
            wprintf(L"~~~~ Ignoring %ls (root is not watched)\n", changedPath);<commit_msg>Polish iteration<commit_after>#ifdef _WIN32

#include "native.h"
#include "generic.h"
#include "win.h"
#include <vector>

using namespace std;

// TODO Find the right size for this
#define EVENT_BUFFER_SIZE (16*1024)

#define CREATE_SHARE (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
#define CREATE_FLAGS (FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED)
#define EVENT_MASK (FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | \
                    FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE)

class Server {
public:
    Server(
        JavaVM *jvm,
        JNIEnv *env,
        jobject watcherCallback,
        wchar_t drivePath[4],
        vector<wchar_t*>* watchedPaths
    ) {
        this->jvm = jvm;
        this->watcherCallback = env->NewGlobalRef(watcherCallback);
        this->stopEventHandle = CreateEvent(NULL, FALSE, FALSE, NULL);
        this->watchedPaths = watchedPaths;
        wcscpy_s(this->drivePath, 4, drivePath);

        this->threadHandle = CreateThread(
            NULL,                   // default security attributes
            0,                      // use default stack size
            EventProcessingThread,  // thread function name
            this,                   // argument to thread function
            0,                      // use default creation flags
            NULL                    // the thread identifier
        );
        SetThreadPriority(this->threadHandle, THREAD_PRIORITY_ABOVE_NORMAL);
    }

    void close(JNIEnv *env) {
        SetEvent(this->stopEventHandle);
        WaitForSingleObject(this->threadHandle, INFINITE);
        CloseHandle(this->threadHandle);
        CloseHandle(this->stopEventHandle);
        for (auto watchedPath : *watchedPaths) {
            free(watchedPath);
        }
        delete watchedPaths;
        env->DeleteGlobalRef(this->watcherCallback);
    }

    static DWORD WINAPI EventProcessingThread(LPVOID data) {
        Server *server = (Server*) data;
        server->run();
        return 0;
    }

private:
    JavaVM *jvm;
    wchar_t drivePath[4];
    vector<wchar_t*>* watchedPaths;
    jobject watcherCallback;

    HANDLE stopEventHandle;
    HANDLE threadHandle;

    void run() {
        printf("~~~~ Starting thread\n");

        // TODO Extract this logic to some shared function
        JNIEnv *env;
        jint statAttach = jvm->AttachCurrentThreadAsDaemon((void **) &(env), NULL);
        if (statAttach != JNI_OK) {
            printf("Failed to attach JNI to current thread: %d\n", statAttach);
            return;
        }

        OVERLAPPED overlapped;
        memset(&overlapped, 0, sizeof(overlapped));
        overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

        HANDLE hDrive = CreateFileW(drivePath, GENERIC_READ, CREATE_SHARE, NULL, OPEN_EXISTING, CREATE_FLAGS, NULL);

        char buffer[EVENT_BUFFER_SIZE];
        HANDLE handles[2] = {stopEventHandle, overlapped.hEvent};
        while (true) {
            int rcDrive = ReadDirectoryChangesW(
                hDrive,
                buffer,
                sizeof(buffer),
                TRUE,
                EVENT_MASK,
                NULL,
                &overlapped,
                NULL
            );
            if (rcDrive == 0) {
                // TODO Error handling
                printf("~~~~ Couldn't read directory: %d - %d\n", rcDrive, GetLastError());
                break;
            }

            DWORD rc = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
            if (rc == WAIT_OBJECT_0) {
                break;
            }
            if (rc == WAIT_OBJECT_0 + 1) {
                DWORD dwBytesReturned;
                if (!GetOverlappedResult(hDrive, &overlapped, &dwBytesReturned, FALSE)) {
                    // TODO Error handling
                    printf("~~~~ Failed to wait\n");
                    break;
                }

                if (dwBytesReturned == 0) {
                    // don't send dirty too much, everything is changed anyway
                    // TODO Understand what this does
                    if (WaitForSingleObject(stopEventHandle, 500) == WAIT_OBJECT_0)
                        break;

                    // Got a buffer overflow => current changes lost => send INVALIDATE on root
                    reportEvent(env, FILE_EVENT_INVALIDATE, drivePath, 3);
                } else {
                    FILE_NOTIFY_INFORMATION *info = (FILE_NOTIFY_INFORMATION *)buffer;
                    do {
                        handlePathChanged(env, info);
                        info = (FILE_NOTIFY_INFORMATION *)((char *)info + info->NextEntryOffset);
                    } while (info->NextEntryOffset != 0);
                }
            }
        }

        printf("~~~~ Stopping thread\n");

        CloseHandle(overlapped.hEvent);
        CloseHandle(hDrive);

        // TODO Extract this logic to some shared function
        jint statDetach = jvm->DetachCurrentThread();
        if (statDetach != JNI_OK) {
            printf("Failed to detach JNI from current thread: %d\n", statAttach);
            return;
        }

        return;
    }

    void reportEvent(JNIEnv *env, jint type, wchar_t *changedPath, int changedPathLen) {
        jstring changedPathJava = wchar_to_java(env, changedPath, changedPathLen, NULL);
        jclass callback_class = env->GetObjectClass(watcherCallback);
        jmethodID methodCallback = env->GetMethodID(callback_class, "pathChanged", "(ILjava/lang/String;)V");
        // TODO Do we need to add a global reference to the string here?
        env->CallVoidMethod(watcherCallback, methodCallback, type, changedPathJava);
    }

    void handlePathChanged(JNIEnv *env, FILE_NOTIFY_INFORMATION *info) {
        int pathLen = info->FileNameLength / sizeof(wchar_t);
        wchar_t *changedPath = add_prefix(info->FileName, pathLen, drivePath);
        int changedPathLen = pathLen + 3;

        wprintf(L"~~~~ Changed: 0x%x %ls\n", info->Action, changedPath);

        jint type;
        if (info->Action == FILE_ACTION_ADDED || info->Action == FILE_ACTION_RENAMED_NEW_NAME) {
            type = FILE_EVENT_CREATED;
        } else if (info->Action == FILE_ACTION_REMOVED || info->Action == FILE_ACTION_RENAMED_OLD_NAME) {
            type = FILE_EVENT_REMOVED;
        } else if (info->Action == FILE_ACTION_MODIFIED) {
            type = FILE_EVENT_MODIFIED;
        } else {
            wprintf(L"~~~~ Unknown event 0x%x for %ls\n", info->Action, changedPath);
            type = FILE_EVENT_UNKNOWN;
        }

        bool watching = false;
        for (auto watchedPath : *watchedPaths) {
            wprintf(L"~~~~ Checking if '%ls' starts with '%ls'\n", changedPath, watchedPath);
            if (wcsncmp(watchedPath, changedPath, wcslen(watchedPath)) == 0) {
                watching = true;
                break;
            }
        }
        if (!watching) {
            wprintf(L"~~~~ Ignoring %ls (root is not watched)\n", changedPath);
{'fraction_non_alphanumeric': 0.10045887751500177, 'fraction_numerical': 0.004518178609248147, 'mean_word_length': 1.9660804020100502, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 79, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4506329', 'n_tokens_mistral': 4125, 'n_tokens_neox': 3904, 'n_words': 947}