<commit_before>//
//  NWLCore.m
//  NWLogging
//
//  Created by leonard on 4/25/12.
//  Copyright (c) 2012 noodlewerk. All rights reserved.
//

#import "NWLCore.h"
#include <stdio.h>
#include <string.h>
#include <math.h>
#import <CoreFoundation/CFDate.h>

#pragma mark - Constants and statics

static const int kNWLFilterListSize = 16;
static const int kNWLPrinterListSize = 8;

typedef struct {
    const char *properties[kNWLProperty_count];
    NWLAction action;
} NWLFilter;

typedef struct {
    int count;
    NWLFilter elements[kNWLFilterListSize];
} NWLFilterList;

typedef struct {
    const char *name;
    void(*func)(NWLContext, CFStringRef, void *);
    void *info;
} NWLPrinter;

typedef struct {
    int count;
    NWLPrinter elements[kNWLPrinterListSize];
} NWLPrinterList;

#define NWLDefaultPrinterName "default"
static NWLFilterList NWLFilters = {1, {NULL, "warn", NULL, NULL, NULL, kNWLAction_print}};
static NWLPrinterList NWLPrinters = {1, {NWLDefaultPrinterName, NWLDefaultPrinter, NULL}};
static CFTimeInterval NWLTimeOffset = 0;


#pragma mark - Printing

void NWLForwardToPrinters(NWLContext context, CFStringRef message) {
    for (int i = 0; i < NWLPrinters.count; i++) {
        NWLPrinter *printer = &NWLPrinters.elements[i];
        void(*func)(NWLContext, CFStringRef, void *) = printer->func;
        func(context, message, printer->info);
    }
}

int NWLAddPrinter(const char *name, void(*func)(NWLContext, CFStringRef, void *), void *info) {
    int count = NWLPrinters.count;
    if (count < kNWLPrinterListSize) {
        NWLPrinters.elements[count].name = name;
        NWLPrinters.elements[count].func = func;
        NWLPrinters.elements[count].info = info;
        NWLPrinters.count = count + 1;
        return true;
    }
    return false;
}

void * NWLRemovePrinter(const char *name) {
    for (int i = NWLPrinters.count - 1; i >= 0; i--) {
        NWLPrinter *p = &NWLPrinters.elements[i];
        const char *n = p->name;
        if (n == name || (n && name && !strcasecmp(n, name))) {
            int count = NWLPrinters.count;
            if (count > 0) {
                void *info = p->info;
                NWLPrinters.count = count - 1;
                NWLPrinters.elements[i] = NWLPrinters.elements[count - 1];
                return info;
            }
        }
    }
    return NULL;
}

void NWLRemoveAllPrinters(void) {
    NWLPrinters.count = 0;
}

void NWLRestoreDefaultPrinters(void) {
    NWLPrinters.elements[0].name = NWLDefaultPrinterName;
    NWLPrinters.elements[0].func = NWLDefaultPrinter;
    NWLPrinters.elements[0].info = NULL;
    NWLPrinters.count = 1;
}

void NWLAddDefaultPrinter(void) {
    NWLAddPrinter(NWLDefaultPrinterName, NWLDefaultPrinter, NULL);
}

void NWLDefaultPrinter(NWLContext context, CFStringRef message, void *info) {
    // compose time
    int hour = 0, minute = 0, second = 0, micro = 0;
    NWLClock(&hour, &minute, &second, &micro);
    // prepare tags
    int hasLib = context.lib && *context.lib;
    int hasFile = context.file && context.line;
    int hasTag = context.tag && *context.tag;
    char lineBuffer[6];
    if (context.line < 1000) {
        snprintf(lineBuffer, 6, "%03u", context.line);
    } else {
        snprintf(lineBuffer, 6, "%06u", context.line);
    }
    // convert to cstring
    unsigned char buffer[256];
    CFIndex used = 0;
    CFRange range = CFRangeMake(0, CFStringGetLength(message));
    CFIndex done = CFStringGetBytes(message, range, kCFStringEncodingUTF8, '?', false, buffer, sizeof(buffer), &used);
    // print log line
    fprintf(stderr, "[%02i:%02i:%02i.%06i%s%s%s%s%s%s%s%s] %.*s%s", 
            //                           %s%s = " lib"
            //                               %s%s%s%s = " file:line"
            //                                       %s%s = "] [tag"
            hour, minute, second, micro, 
            (hasLib ? " " : ""), (hasLib ? context.lib : ""), 
            (hasFile ? " " : ""), (hasFile ? context.file : ""), (hasFile ? ":" : ""), (hasFile ? lineBuffer : ""), 
            (hasTag ? "] [" : ""), (hasTag ? context.tag : ""), 
            (int)used, buffer, done >= range.length ? "\n" : (done ? "" : "~\n"));
    // while string exceeded buffer, print remaining chunks
    while (done && done < range.length) {
        range.location += done;
        range.length -= done;
        done = CFStringGetBytes(message, range, kCFStringEncodingUTF8, '?', false, buffer, sizeof(buffer), &used);
        fprintf(stderr, "%.*s%s", (int)used, buffer, done >= range.length ? "\n" : (done ? "" : "~\n"));
    }
}


#pragma mark - Filtering

NWLAction NWLMatchingActionForContext(NWLContext context) {
    NWLAction result = kNWLAction_none;
    int bestScore = 0;
    for (int i = 0; i < NWLFilters.count; i++) {
        NWLFilter *filter = &NWLFilters.elements[i];
        if (result < filter->action) {
            int score = 0;
            const char *s = NULL;
#define _NWL_FIND_(_prop) s = filter->properties[kNWLProperty_##_prop]; if (s && context._prop) {if (strcasecmp(s, context._prop)) {continue;} else {score++;}}
            _NWL_FIND_(tag)
            _NWL_FIND_(lib)
            _NWL_FIND_(file)
            _NWL_FIND_(function)
            if (bestScore <= score) {
                bestScore = score;
                result = filter->action;
            }<commit_msg>switched to using writev in default printer<commit_after>//
//  NWLCore.m
//  NWLogging
//
//  Created by leonard on 4/25/12.
//  Copyright (c) 2012 noodlewerk. All rights reserved.
//

#import "NWLCore.h"
#include <stdio.h>
#include <string.h>
#include <sys/uio.h>
#include <math.h>
#import <CoreFoundation/CFDate.h>

#pragma mark - Constants and statics

static const int kNWLFilterListSize = 16;
static const int kNWLPrinterListSize = 8;

typedef struct {
    const char *properties[kNWLProperty_count];
    NWLAction action;
} NWLFilter;

typedef struct {
    int count;
    NWLFilter elements[kNWLFilterListSize];
} NWLFilterList;

typedef struct {
    const char *name;
    void(*func)(NWLContext, CFStringRef, void *);
    void *info;
} NWLPrinter;

typedef struct {
    int count;
    NWLPrinter elements[kNWLPrinterListSize];
} NWLPrinterList;

#define NWLDefaultPrinterName "default"
static NWLFilterList NWLFilters = {1, {NULL, "warn", NULL, NULL, NULL, kNWLAction_print}};
static NWLPrinterList NWLPrinters = {1, {NWLDefaultPrinterName, NWLDefaultPrinter, NULL}};
static CFTimeInterval NWLTimeOffset = 0;


#pragma mark - Printing

void NWLForwardToPrinters(NWLContext context, CFStringRef message) {
    for (int i = 0; i < NWLPrinters.count; i++) {
        NWLPrinter *printer = &NWLPrinters.elements[i];
        void(*func)(NWLContext, CFStringRef, void *) = printer->func;
        func(context, message, printer->info);
    }
}

int NWLAddPrinter(const char *name, void(*func)(NWLContext, CFStringRef, void *), void *info) {
    int count = NWLPrinters.count;
    if (count < kNWLPrinterListSize) {
        NWLPrinters.elements[count].name = name;
        NWLPrinters.elements[count].func = func;
        NWLPrinters.elements[count].info = info;
        NWLPrinters.count = count + 1;
        return true;
    }
    return false;
}

void * NWLRemovePrinter(const char *name) {
    for (int i = NWLPrinters.count - 1; i >= 0; i--) {
        NWLPrinter *p = &NWLPrinters.elements[i];
        const char *n = p->name;
        if (n == name || (n && name && !strcasecmp(n, name))) {
            int count = NWLPrinters.count;
            if (count > 0) {
                void *info = p->info;
                NWLPrinters.count = count - 1;
                NWLPrinters.elements[i] = NWLPrinters.elements[count - 1];
                return info;
            }
        }
    }
    return NULL;
}

void NWLRemoveAllPrinters(void) {
    NWLPrinters.count = 0;
}

void NWLRestoreDefaultPrinters(void) {
    NWLPrinters.elements[0].name = NWLDefaultPrinterName;
    NWLPrinters.elements[0].func = NWLDefaultPrinter;
    NWLPrinters.elements[0].info = NULL;
    NWLPrinters.count = 1;
}

void NWLAddDefaultPrinter(void) {
    NWLAddPrinter(NWLDefaultPrinterName, NWLDefaultPrinter, NULL);
}

void NWLDefaultPrinter(NWLContext context, CFStringRef message, void *info) {
    // init io vector
    struct iovec iov[16];
    int i = 0;
    iov[i].iov_base = "[";
    iov[i++].iov_len = 1;

    // add time
    int hour = 0, minute = 0, second = 0, micro = 0;
    NWLClock(&hour, &minute, &second, &micro);
    char timeBuffer[16];
    int timeLength = snprintf(timeBuffer, sizeof(timeBuffer), "%02i:%02i:%02i.%06i", hour, minute, second, micro);
    iov[i].iov_base = timeBuffer;
    iov[i++].iov_len = sizeof(timeBuffer) - 1 < timeLength ? sizeof(timeBuffer) - 1 : timeLength;
    
    // add context
    if (context.lib && *context.lib) {
        iov[i].iov_base = " ";
        iov[i++].iov_len = 1;
        iov[i].iov_base = (void *)context.lib;
        iov[i++].iov_len = strnlen(context.lib, 32);
    }
    if (context.file && *context.file) {
        iov[i].iov_base = " ";
        iov[i++].iov_len = 1;
        iov[i].iov_base = (void *)context.file;
        iov[i++].iov_len = strnlen(context.file, 32);
        iov[i].iov_base = ":";
        iov[i++].iov_len = 1;
        char lineBuffer[10];
        int lineLength = snprintf(lineBuffer, sizeof(lineBuffer), context.line < 1000 ? "%03u" : "%06u", context.line);
        iov[i].iov_base = lineBuffer;
        iov[i++].iov_len = sizeof(lineBuffer) - 1 < lineLength ? sizeof(lineBuffer) - 1 : lineLength;
    }
    if (context.tag && *context.tag) {
        iov[i].iov_base = "] [";
        iov[i++].iov_len = 3;
        iov[i].iov_base = (void *)context.tag;
        iov[i++].iov_len = strnlen(context.tag, 32);
    }
    
    iov[i].iov_base = "] ";
    iov[i++].iov_len = 2;

    CFRange range = CFRangeMake(0, CFStringGetLength(message));
    if (range.length) {
        // add message
        unsigned char messageBuffer[256];
        CFIndex messageLength = 0;
        CFIndex length = 1;
        
        while (length && range.length) {
            length = CFStringGetBytes(message, range, kCFStringEncodingUTF8, '?', false, messageBuffer, sizeof(messageBuffer), &messageLength);
            iov[i].iov_base = messageBuffer;
            iov[i++].iov_len = messageLength;
            if (length >= range.length) {
                iov[i].iov_base = "\n";
                iov[i++].iov_len = 1;
            } else if (!length) {
                iov[i].iov_base = "~\n";
                iov[i++].iov_len = 2;
            }
            writev(STDERR_FILENO, iov, i);
            i = 0;
            range.location += length;
            range.length -= length;
        }
    } else {
        iov[i].iov_base = "\n";
        iov[i++].iov_len = 1;
        writev(STDERR_FILENO, iov, i);
    }
}


#pragma mark - Filtering

NWLAction NWLMatchingActionForContext(NWLContext context) {
    NWLAction result = kNWLAction_none;
    int bestScore = 0;
    for (int i = 0; i < NWLFilters.count; i++) {
        NWLFilter *filter = &NWLFilters.elements[i];
        if (result < filter->action) {
            int score = 0;
            const char *s = NULL;
#define _NWL_FIND_(_prop) s = filter->properties[kNWLProperty_##_prop]; if (s && context._prop) {if (strcasecmp(s, context._prop)) {continue;} else {score++;}}
            _NWL_FIND_(tag)
            _NWL_FIND_(lib)
            _NWL_FIND_(file)
            _NWL_FIND_(function)
            if (bestScore <= score) {
                bestScore = score;
                result = filter->action;
            }
{'fraction_non_alphanumeric': 0.13913870632998962, 'fraction_numerical': 0.012452438602559669, 'mean_word_length': 2.5226926591532135, 'pattern_counts': {'":': 2, '<': 27, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1166056', 'n_tokens_mistral': 4069, 'n_tokens_neox': 3944, 'n_words': 868}