<commit_before>/*
 * Copyright (c) 2015 <NAME>
 *
 * 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 <errno.h>
#include <string.h>
#include <unistd.h>

#include <nan.h>

/*
 * Try to check a given argument is actually a `net.Socket` instance. This is
 * not a strict check and can be easily be fooled. But at least, it should
 * prevent some trivial programming errors.
 */
static bool looks_like_a_socket(v8::Local<v8::Value> object) {
    if (!object->IsObject())
        return false;
    v8::Local<v8::Object> socket = object.As<v8::Object>();

    v8::Local<v8::String> className = socket->GetConstructorName();
    if (strcmp("Socket", *Nan::Utf8String(className->ToString())))
        return false;

    return true;
}

/*
 * Checks if the socket has the 'readable' property set to true.
 */
static bool socket_is_readable(v8::Local<v8::Object> socket) {
    v8::Local<v8::String> key =
            Nan::New<v8::String>("readable").ToLocalChecked();

    if (!socket->Has(key))
        return false;

    v8::Local<v8::Value> value = socket->Get(key);
    return value->IsBoolean() && Nan::To<bool>(value).FromJust();
}

/*
 * Make sure the passed socket has a TCP handle with an associated fd. Returns
 * the fd on success, or -1 in case of error.
 */
static int get_fd_from_socket(v8::Local<v8::Object> socket) {
    v8::Local<v8::String> key;
    v8::Local<v8::Object> handle;
    v8::Local<v8::Value> value;
    v8::Local<v8::String> className;

    key = Nan::New<v8::String>("_handle").ToLocalChecked();
    if (!socket->Has(key))
        return -1;

    value = socket->Get(key);
    if (!value->IsObject())
        return -1;
    handle = value.As<v8::Object>();

    className = handle->GetConstructorName();
    if (strcmp("TCP", *Nan::Utf8String(className->ToString())))
        return -1;

    key = Nan::New<v8::String>("fd").ToLocalChecked();
    if (!handle->Has(key))
        return -1;

    value = handle->Get(key);
    if (!value->IsNumber())
        return -1;

    int fd = Nan::To<int>(value).FromJust();

    if (fd < 0)
        return -1;

    return fd;
}

class PosixReadWorker : public Nan::AsyncWorker {
 private:
    int fd;
    bool fd_was_non_blocking;

    size_t size;
    v8::Local<v8::Object> buffer;
    char *data;

    /*
     * Set the socket blocking, if it was not.
     */
    int set_blocking() {
        int opts = fcntl(fd, F_GETFL);
        if (opts == -1)
            return -1;

        fd_was_non_blocking = opts & O_NONBLOCK;

        if (fd_was_non_blocking) {
            opts &= ~O_NONBLOCK;
            if (fcntl(fd, F_SETFL, opts) == -1)
                return -1;
        }

        return 0;
    }

    /*
     * Reset the socket like in the mode (blocking vs. non-blocking) it was.
     */
    int unset_blocking() {
        if (fd_was_non_blocking) {
            int opts = fcntl(fd, F_GETFL);
            if (opts == -1)
                return -1;

            opts |= O_NONBLOCK;
            if (fcntl(fd, F_SETFL, opts) == -1)
                return -1;
        }

        return 0;
    }

 public:
    PosixReadWorker(Nan::Callback *callback, int fd, size_t size)
            : Nan::AsyncWorker(callback), fd(fd), size(size) {
        buffer = Nan::NewBuffer((uint32_t) size).ToLocalChecked();
        data = node::Buffer::Data(buffer);
    }

    ~PosixReadWorker() {}

    /*
     * Executed inside the worker-thread. It is not safe to access V8, or V8
     * data structures here, so everything we need for input and output should
     * go on `this`.
     */
    void Execute() {
        static char msg[256];
        size_t count = 0;

        // TODO(adrienverge): Double check that it's really not needed to free
        // buffer when returning in case of error
        if (set_blocking()) {
            snprintf(msg, sizeof(msg), "fnctl failed: %s", strerror(errno));
            SetErrorMessage(msg);
            return;
        }

        do {
            ssize_t n = read(fd, &data[count], size - count);
            if (n == -1) {
                if (errno == EINTR)
                    continue;

                snprintf(msg, sizeof(msg), "read failed: %s", strerror(errno));
                SetErrorMessage(msg);
                break;
            } else if (n == 0) {  // end of stream
                snprintf(msg, sizeof(msg),
                        "reached end of stream (read %lu bytes)", count);
                SetErrorMessage(msg);
                break;
            } else {
                count += n;
            }
        } while (count < size);

        if (unset_blocking()) {
            snprintf(msg, sizeof(msg), "fnctl failed: %s", strerror(errno));
            SetErrorMessage(msg);
        }
    }

    /*
     * Executed when the async work is complete this function will be run<commit_msg>Do not overwrite error message when already set<commit_after>/*
 * Copyright (c) 2015 <NAME>
 *
 * 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 <errno.h>
#include <string.h>
#include <unistd.h>

#include <nan.h>

/*
 * Try to check a given argument is actually a `net.Socket` instance. This is
 * not a strict check and can be easily be fooled. But at least, it should
 * prevent some trivial programming errors.
 */
static bool looks_like_a_socket(v8::Local<v8::Value> object) {
    if (!object->IsObject())
        return false;
    v8::Local<v8::Object> socket = object.As<v8::Object>();

    v8::Local<v8::String> className = socket->GetConstructorName();
    if (strcmp("Socket", *Nan::Utf8String(className->ToString())))
        return false;

    return true;
}

/*
 * Checks if the socket has the 'readable' property set to true.
 */
static bool socket_is_readable(v8::Local<v8::Object> socket) {
    v8::Local<v8::String> key =
            Nan::New<v8::String>("readable").ToLocalChecked();

    if (!socket->Has(key))
        return false;

    v8::Local<v8::Value> value = socket->Get(key);
    return value->IsBoolean() && Nan::To<bool>(value).FromJust();
}

/*
 * Make sure the passed socket has a TCP handle with an associated fd. Returns
 * the fd on success, or -1 in case of error.
 */
static int get_fd_from_socket(v8::Local<v8::Object> socket) {
    v8::Local<v8::String> key;
    v8::Local<v8::Object> handle;
    v8::Local<v8::Value> value;
    v8::Local<v8::String> className;

    key = Nan::New<v8::String>("_handle").ToLocalChecked();
    if (!socket->Has(key))
        return -1;

    value = socket->Get(key);
    if (!value->IsObject())
        return -1;
    handle = value.As<v8::Object>();

    className = handle->GetConstructorName();
    if (strcmp("TCP", *Nan::Utf8String(className->ToString())))
        return -1;

    key = Nan::New<v8::String>("fd").ToLocalChecked();
    if (!handle->Has(key))
        return -1;

    value = handle->Get(key);
    if (!value->IsNumber())
        return -1;

    int fd = Nan::To<int>(value).FromJust();

    if (fd < 0)
        return -1;

    return fd;
}

class PosixReadWorker : public Nan::AsyncWorker {
 private:
    int fd;
    bool fd_was_non_blocking;

    size_t size;
    v8::Local<v8::Object> buffer;
    char *data;

    /*
     * Set the socket blocking, if it was not.
     */
    int set_blocking() {
        int opts = fcntl(fd, F_GETFL);
        if (opts == -1)
            return -1;

        fd_was_non_blocking = opts & O_NONBLOCK;

        if (fd_was_non_blocking) {
            opts &= ~O_NONBLOCK;
            if (fcntl(fd, F_SETFL, opts) == -1)
                return -1;
        }

        return 0;
    }

    /*
     * Reset the socket like in the mode (blocking vs. non-blocking) it was.
     */
    int unset_blocking() {
        if (fd_was_non_blocking) {
            int opts = fcntl(fd, F_GETFL);
            if (opts == -1)
                return -1;

            opts |= O_NONBLOCK;
            if (fcntl(fd, F_SETFL, opts) == -1)
                return -1;
        }

        return 0;
    }

 public:
    PosixReadWorker(Nan::Callback *callback, int fd, size_t size)
            : Nan::AsyncWorker(callback), fd(fd), size(size) {
        buffer = Nan::NewBuffer((uint32_t) size).ToLocalChecked();
        data = node::Buffer::Data(buffer);
    }

    ~PosixReadWorker() {}

    /*
     * Executed inside the worker-thread. It is not safe to access V8, or V8
     * data structures here, so everything we need for input and output should
     * go on `this`.
     */
    void Execute() {
        static char msg[256];
        size_t count = 0;

        // TODO(adrienverge): Double check that it's really not needed to free
        // buffer when returning in case of error
        if (set_blocking()) {
            snprintf(msg, sizeof(msg), "fnctl failed: %s", strerror(errno));
            SetErrorMessage(msg);
            return;
        }

        do {
            ssize_t n = read(fd, &data[count], size - count);
            if (n == -1) {
                if (errno == EINTR)
                    continue;

                snprintf(msg, sizeof(msg), "read failed: %s", strerror(errno));
                SetErrorMessage(msg);
                break;
            } else if (n == 0) {  // end of stream
                snprintf(msg, sizeof(msg),
                        "reached end of stream (read %lu bytes)", count);
                SetErrorMessage(msg);
                break;
            } else {
                count += n;
            }
        } while (count < size);

        if (unset_blocking()) {
            if (ErrorMessage() == NULL) {
                snprintf(msg, sizeof(msg), "fnctl failed: %s", strerror(errno));
                SetErrorMessage(msg);
            }
        }
    }

    /*
     * Executed when the async work is complete this function will be run
{'fraction_non_alphanumeric': 0.12253569000679809, 'fraction_numerical': 0.01070700203942896, 'mean_word_length': 2.328337104072398, 'pattern_counts': {'":': 0, '<': 55, '<?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': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3475740', 'n_tokens_mistral': 3637, 'n_tokens_neox': 3455, 'n_words': 1147}