<commit_before>//
//  src/macho/container.cc
//  tbd
//
//  Created by inoahdev on 4/24/17.
//  Copyright © 2017 inoahdev. All rights reserved.
//

#include <cstdio>
#include <cstdlib>

#include "headers/symbol_table.h"
#include "container.h"

namespace macho {
    container::container(FILE *file, long base)
    : file_(file), base_(base) {
        const auto position = ftell(file);
        fseek(file, 0, SEEK_END);

        this->size_ = ftell(file);

        fseek(file, position, SEEK_SET);
        this->validate();
    }

    container::container(FILE *file, long base, size_t size)
    : file_(file), base_(base), size_(size) {
        this->validate();
    }

    container::~container() {
        auto &cached_load_commands = cached_load_commands_;
        if (cached_load_commands) {
            delete[] cached_load_commands;
        }

        auto &cached_string_table = cached_string_table_;
        if (cached_string_table) {
            delete[] cached_string_table;
        }

        auto &cached_symbol_table = cached_symbol_table_;
        if (cached_symbol_table) {
            delete[] cached_symbol_table;
        }
    }

    void container::validate() {
        auto &base = base_;
        auto &file = file_;

        auto &header = header_;
        auto &magic = header.magic;

        const auto is_big_endian = this->is_big_endian();
        const auto position = ftell(file);

        fseek(file, base, SEEK_SET);
        fread(&magic, sizeof(uint32_t), 1, file);

        const auto macho_file_is_regular = magic_is_thin(magic);
        if (macho_file_is_regular) {
            fread(&header.cputype, sizeof(header) - sizeof(uint32_t), 1, file);

            if (is_big_endian) {
                swap_mach_header(&header);
            }
        } else {
            const auto macho_file_is_fat = magic_is_fat(magic);
            if (macho_file_is_fat) {
                fprintf(stderr, "Architecture at location (0x%.8lX) cannot be a fat mach-o file itself\n", base);
                exit(1);
            } else {
                fprintf(stderr, "Architecture at location (0x%.8lX) is not a valid mach-o base\n", base);
                exit(1);
            }
        }

        fseek(file, position, SEEK_SET);
    }

    void container::iterate_load_commands(const std::function<bool (const struct load_command *, const struct load_command *)> &callback) {
        const auto &base = base_;
        const auto &file = file_;

        const auto &header = header_;
        const auto is_big_endian = this->is_big_endian();

        const auto &ncmds = header.ncmds;
        const auto &sizeofcmds = header.sizeofcmds;

        auto &cached_load_commands = cached_load_commands_;
        auto created_load_commands_cache = !cached_load_commands_;

        if (created_load_commands_cache) {
            cached_load_commands = new uint8_t[sizeofcmds];

            auto load_command_base = base + sizeof(header);
            if (this->is_64_bit()) {
                load_command_base += sizeof(uint32_t);
            }

            const auto position = ftell(file);

            fseek(file, load_command_base, SEEK_SET);
            fread(cached_load_commands, sizeofcmds, 1, file);

            fseek(file, position, SEEK_SET);
        }

        auto size_used = 0;
        auto should_callback = true;

        for (auto i = 0, cached_load_commands_index = 0; i < ncmds; i++) {
            auto load_cmd = (struct load_command *)&cached_load_commands[cached_load_commands_index];
            const auto &cmdsize = load_cmd->cmdsize;

            if (created_load_commands_cache) {
                auto load_command = *load_cmd;
                if (is_big_endian) {
                    swap_load_command(&load_command);
                }

                const auto &cmdsize = load_command.cmdsize;
                if (cmdsize < sizeof(struct load_command)) {
                    fprintf(stderr, "Load-command (at index %d) of mach-o container (at base 0x%.8lX) is too small to be valid\n", i, base);
                    exit(1);
                }

                if (cmdsize >= sizeofcmds) {
                    fprintf(stderr, "Load-command (at index %d) of mach-o container (at base 0x%.8lX) is larger than/or equal to entire area allocated for load-commands\n", i, base);
                    exit(1);
                }

                size_used += cmdsize;
                if (size_used > sizeofcmds) {
                    fprintf(stderr, "Load-command (at index %d) of mach-o container (at base 0x%.8lX) goes past end of area allocated for load-commands\n", i, base);
                    exit(1);
                } else if (size_used == sizeofcmds && i != ncmds - 1) {
                    fprintf(stderr, "Load-command (at index %d) of mach-o container (at base 0x%.8lX) takes up all of the remaining space allocated for load-commands\n", i, base);
                    exit(1);
                }
                
                if (should_callback) {
                    should_callback = callback(&load_command, load_cmd);
                }
            } else {
                if (should_callback) {
                    should_callback = callback(load_cmd, load_cmd);
                }
            }

            if (!should_callback) {
                if (!created_load_commands_cache) {
                    break;
                }
            }

            cached_load_commands_index += cmdsize;
        }
    }

    void container::iterate_symbols(const std::function<bool (const struct nlist_64 &, const char *)> &callback) {
        auto &base = base_;
        auto &size = size_;

        auto &file = file_;

        const auto is_big_endian = this->is_big_endian();
        const auto position = ftell(file);

        auto &symbol_table = symbol_table_;
        if (!symbol_table) {
            iterate_load_commands([&](const struct load_command *load_cmd, const struct load_command *load_command) {
                if (load_cmd->cmd != load_commands::symbol_table) {
                    return true;
                }

                symbol_table = (struct symtab_command *)load_command;
                if (is_big_endian) {
                    swap_symtab_command(symbol_table);
                }

                return false;
            });<commit_msg>Simplify logic for calling-back in iterate_load_commands<commit_after>//
//  src/macho/container.cc
//  tbd
//
//  Created by inoahdev on 4/24/17.
//  Copyright © 2017 inoahdev. All rights reserved.
//

#include <cstdio>
#include <cstdlib>

#include "headers/symbol_table.h"
#include "container.h"

namespace macho {
    container::container(FILE *file, long base)
    : file_(file), base_(base) {
        const auto position = ftell(file);
        fseek(file, 0, SEEK_END);

        this->size_ = ftell(file);

        fseek(file, position, SEEK_SET);
        this->validate();
    }

    container::container(FILE *file, long base, size_t size)
    : file_(file), base_(base), size_(size) {
        this->validate();
    }

    container::~container() {
        auto &cached_load_commands = cached_load_commands_;
        if (cached_load_commands) {
            delete[] cached_load_commands;
        }

        auto &cached_string_table = cached_string_table_;
        if (cached_string_table) {
            delete[] cached_string_table;
        }

        auto &cached_symbol_table = cached_symbol_table_;
        if (cached_symbol_table) {
            delete[] cached_symbol_table;
        }
    }

    void container::validate() {
        auto &base = base_;
        auto &file = file_;

        auto &header = header_;
        auto &magic = header.magic;

        const auto is_big_endian = this->is_big_endian();
        const auto position = ftell(file);

        fseek(file, base, SEEK_SET);
        fread(&magic, sizeof(uint32_t), 1, file);

        const auto macho_file_is_regular = magic_is_thin(magic);
        if (macho_file_is_regular) {
            fread(&header.cputype, sizeof(header) - sizeof(uint32_t), 1, file);

            if (is_big_endian) {
                swap_mach_header(&header);
            }
        } else {
            const auto macho_file_is_fat = magic_is_fat(magic);
            if (macho_file_is_fat) {
                fprintf(stderr, "Architecture at location (0x%.8lX) cannot be a fat mach-o file itself\n", base);
                exit(1);
            } else {
                fprintf(stderr, "Architecture at location (0x%.8lX) is not a valid mach-o base\n", base);
                exit(1);
            }
        }

        fseek(file, position, SEEK_SET);
    }

    void container::iterate_load_commands(const std::function<bool (const struct load_command *, const struct load_command *)> &callback) {
        const auto &base = base_;
        const auto &file = file_;

        const auto &header = header_;
        const auto is_big_endian = this->is_big_endian();

        const auto &ncmds = header.ncmds;
        const auto &sizeofcmds = header.sizeofcmds;

        auto &cached_load_commands = cached_load_commands_;
        auto created_load_commands_cache = !cached_load_commands_;

        if (created_load_commands_cache) {
            cached_load_commands = new uint8_t[sizeofcmds];

            auto load_command_base = base + sizeof(header);
            if (this->is_64_bit()) {
                load_command_base += sizeof(uint32_t);
            }

            const auto position = ftell(file);

            fseek(file, load_command_base, SEEK_SET);
            fread(cached_load_commands, sizeofcmds, 1, file);

            fseek(file, position, SEEK_SET);
        }

        auto size_used = 0;
        auto should_callback = true;

        for (auto i = 0, cached_load_commands_index = 0; i < ncmds; i++) {
            auto load_cmd = (struct load_command *)&cached_load_commands[cached_load_commands_index];
            const auto &cmdsize = load_cmd->cmdsize;

            if (created_load_commands_cache) {
                auto load_command = *load_cmd;
                if (is_big_endian) {
                    swap_load_command(&load_command);
                }

                const auto &cmdsize = load_command.cmdsize;
                if (cmdsize < sizeof(struct load_command)) {
                    fprintf(stderr, "Load-command (at index %d) of mach-o container (at base 0x%.8lX) is too small to be valid\n", i, base);
                    exit(1);
                }

                if (cmdsize >= sizeofcmds) {
                    fprintf(stderr, "Load-command (at index %d) of mach-o container (at base 0x%.8lX) is larger than/or equal to entire area allocated for load-commands\n", i, base);
                    exit(1);
                }

                size_used += cmdsize;
                if (size_used > sizeofcmds) {
                    fprintf(stderr, "Load-command (at index %d) of mach-o container (at base 0x%.8lX) goes past end of area allocated for load-commands\n", i, base);
                    exit(1);
                } else if (size_used == sizeofcmds && i != ncmds - 1) {
                    fprintf(stderr, "Load-command (at index %d) of mach-o container (at base 0x%.8lX) takes up all of the remaining space allocated for load-commands\n", i, base);
                    exit(1);
                }

                if (should_callback) {
                    should_callback = callback(&load_command, load_cmd);
                }
            } else {
                should_callback = callback(load_cmd, load_cmd);
                if (!should_callback) {
                    break;
                }
            }
            
            cached_load_commands_index += cmdsize;
        }
    }

    void container::iterate_symbols(const std::function<bool (const struct nlist_64 &, const char *)> &callback) {
        auto &base = base_;
        auto &size = size_;

        auto &file = file_;

        const auto is_big_endian = this->is_big_endian();
        const auto position = ftell(file);

        auto &symbol_table = symbol_table_;
        if (!symbol_table) {
            iterate_load_commands([&](const struct load_command *load_cmd, const struct load_command *load_command) {
                if (load_cmd->cmd != load_commands::symbol_table) {
                    return true;
                }

                symbol_table = (struct symtab_command *)load_command;
                if (is_big_endian) {
                    swap_symtab_command(symbol_table);
                }

                return false;
            });
{'fraction_non_alphanumeric': 0.100438421681945, 'fraction_numerical': 0.007333599043443603, 'mean_word_length': 1.8474807081252838, 'pattern_counts': {'":': 0, '<': 15, '<?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': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '886840', 'n_tokens_mistral': 3796, 'n_tokens_neox': 3571, 'n_words': 883}