找不到 config.guess 和 config.sub 文件
2025-01-08 15:00:39+08:00

找不到 config.guess 和 config.sub 文件

现象:

configure: error: cannot find required auxiliary files: config.guess config.sub

解决办法:

automake --add-missing
PPM格式说明
2024-12-18 21:18:31+08:00

PPM格式说明

PPM 是一种很方便的图片文件格式,它的结构为:

PPM类型标识
图像宽度 图像高度 颜色的最大值
R G B R G B R G B R G B R G B R G B ...
PPM类型标识 图像类型 数据表示方式
P1 二值图像 文本格式
P2 灰度图像 文本格式
P3 彩色图像 文本格式
P4 二值图像 二进制格式
P5 灰度图像 二进制格式
P6 彩色图像 二进制格式

例如一个下面这张图

P3
2 2 255

255 0 0 0 255 0
0 0 255 0 0 0

ppm

VS Code 下载慢的解决办法
2024-12-18 21:18:31+08:00

VS Code 下载慢的解决办法

将下载链接的域名改为 vscode.cdn.azure.cn,这是微软在国内的 CDN 地址。

例如 https://az764295.vo.msecnd.net/stable/899d46d82c4c95423fb7e10e68eba52050e30ba3/code_1.63.2-1639562499_amd64.deb
改为 https://vscode.cdn.azure.cn/stable/899d46d82c4c95423fb7e10e68eba52050e30ba3/code_1.63.2-1639562499_amd64.deb

可以使用下面的 Python 脚本进行下载:

$ python vscode-download.py --os linux-deb-x64
Request https://code.visualstudio.com/sha/download?build=stable&os=linux-deb-x64
Redirect to https://az764295.vo.msecnd.net/stable/704ed70d4fd1c6bd6342c436f1ede30d1cff4710/code_1.77.3-1681292746_amd64.deb
Redirect to https://vscode.cdn.azure.cn/stable/704ed70d4fd1c6bd6342c436f1ede30d1cff4710/code_1.77.3-1681292746_amd64.deb
Download 88538392/88538392 bytes
Done.
#! /usr/bin/env python3
# author: planc
# e-mail: hubenchang0515@outlook.com
# repo: https://github.com/hubenchang0515/Moe-Maid/blob/master/vscode-download.py

import argparse
import urllib.request
import urllib.parse
from pathlib import Path

os_options: list[str] = [
    "win32-x64-user",
    "win32-user",
    "win32-arm64-user",

    "win32-x64",
    "win32",
    "win32-arm64",

    "win32-x64-archive",
    "win32-archive",
    "win32-arm64-archive",

    "cli-win32-x64",
    "cli-x64",
    "cli-arm64-x64",

    "linux-deb-x64",
    "linux-deb-armhf",
    "linux-deb-arm64",

    "linux-rpm-x64",
    "linux-rpm-armhf",
    "linux-rpm-arm64",

    "linux-x64",
    "linux-armhf",
    "linux-arm64",

    "cli-alpine-x64",
    "cli-alpine-armhf",
    "cli-alpine-arm64",

    "darwin",
    "darwin-arm64",
    "darwin-universal",

    "cli-darwin-x64",
    "cli-darwin-arm64",
]

build_options: list[str] = [
    "stable",
    "insider"
]

class RedirectHandler(urllib.request.HTTPRedirectHandler):
    def __init__(self, cdn) -> None:
        super().__init__()
        self.__cdn = cdn

    def redirect_request(self, req, fp, code, msg, headers, newurl):
        print(f"Redirect to {newurl}")
        parts = urllib.parse.urlparse(newurl)
        parts = parts._replace(netloc=self.__cdn)
        newurl = parts.geturl()
        print(f"Redirect to {newurl}")
        return urllib.request.Request(newurl)

if __name__ == "__main__":
    build_options:str = "\n\t".join(build_options)
    os_options:str = "\n\t".join(os_options)
    parser = argparse.ArgumentParser(description="Download VS Code with CDN", 
                                        epilog=f"build options:\n\t{build_options}\n\nos options:\n\t{os_options}\n",
                                        formatter_class=argparse.RawTextHelpFormatter)
    parser.add_argument("--os", required=True, help="operating system")
    parser.add_argument("--build", required=False, help="build type, default 'stable'", default="stable")
    parser.add_argument("--cdn", required=False, help="CDN url, default 'vscode.cdn.azure.cn'", default="vscode.cdn.azure.cn")
    parser.add_argument("--proxy", required=False, help="proxy, default None")
    args = parser.parse_args()

    if args.proxy is None:
        proxies = None
    else:
        proxies = {
            "http": args.proxy,
            "https": args.proxy,
            "socks5": args.proxy,
        }

    proxy_handler = urllib.request.ProxyHandler(proxies=proxies)
    redirect_handler = RedirectHandler(args.cdn)
    opener = urllib.request.build_opener(proxy_handler, redirect_handler)

    params = urllib.parse.urlencode({'build': args.build, 'os': args.os})
    url:str = f"https://code.visualstudio.com/sha/download?{params}"
    print(f"Request {url}")
    
    with opener.open(url) as response:
        parts = urllib.parse.urlparse(response.geturl())
        path = Path(parts.path)
        total_bytes:int = int(response.getheader('Content-Length'))
        done_bytes:int = 0
        with open(path.name, mode="wb") as fp:
            while done_bytes < total_bytes:
                data = response.read(4*1024)
                if data is None:
                    break
                fp.write(data)
                done_bytes += len(data)
                print(f"Download {done_bytes}/{total_bytes} bytes", end="\r")
            print("\nDone.")
idx-ubyte 文件格式
2024-12-18 21:18:31+08:00

idx-ubyte 文件格式

idx-ubyte 是一种很简单的二进制文件格式,著名的 MNIST 使用的就是该格式。

它由一个 magic-number 和各个维度的长度组成 header,然后是主体数据。magic-number 和维度的长度都是 32 位大端无符号整数。

  • idx1-ubyte 的数据有一个维度,magic-number 的值为 0x00000801
  • idx3-ubyte 的数据有三个维度,magic-number 的值为 0x00000803
struct Idx1Ubyte
{
    uint32_t magicNumber; 
    uint32_t dim1;
    uint8_t datas[];
};

struct Idx3Ubyte
{
    uint32_t magicNumber; 
    uint32_t dim1;
    uint32_t dim2;
    uint32_t dim3;
    uint8_t datas[];
};

以 MNIST 为例 :

  • train-images.idx3-ubyte 是训练集图片
    • 维度 1 的值是 6000,表示包含 6000 张图片
    • 维度 2 的值是 28,表示一张图片有 28 行像素
    • 维度 3 的值是 28,表示一张图片有 28 列像素
  • train-labels.idx1-ubyte 时训练集标注
    • 维度 1 的值是 6000,表示包含 6000 个标注
#ifndef IDX_UBYTE_HPP
#define IDX_UBYTE_HPP

#include <cstdio>
#include <cstdint>
#include <cstring>
#include <cerrno>
#include <vector>

template<uint8_t N>
struct IdxUbyteData
{
    uint8_t* data =  nullptr;
    uint32_t dims[N];

    IdxUbyteData() noexcept = default;

    ~IdxUbyteData() noexcept
    {
        if (data != nullptr)
        {
            delete[] data;
            data = nullptr;
        }
    }

    IdxUbyteData(IdxUbyteData&& src) noexcept
    {
        data = src.data;
        src.data = nullptr;
        memcpy(dims, src.dims, sizeof(dims));
    }

    IdxUbyteData(const IdxUbyteData& src) noexcept
    {
        memcpy(dims, src.dims, sizeof(dims));

        size_t bytes = 1;
        for (uint32_t i = 0; i < N; i++)
        {
            bytes *= dims[i];
        }

        data = new uint8_t[bytes];
        memcpy(data, src.data, bytes);
    }
};

template<uint8_t N>
class IdxUbyte
{
public:
    IdxUbyte() noexcept = default;
    ~IdxUbyte() noexcept = default;

    bool write(const char* file, const std::vector< IdxUbyteData<N-1> >& dataset) const noexcept
    {
        if (dataset.size() == 0)
            return false;

        FILE* fp = fopen(file, "wb");
        if (fp == nullptr)
        {
            fprintf(stderr, "%s\n", strerror(errno));
            return false;
        }

        this->m_write<32>(fp, MagicNumber);
        this->m_write<32>(fp, dataset.size());

        size_t bytes = 1;
        for (uint32_t i = 0; i < N-1; i++)
        {
            this->m_write<32>(fp, dataset[0].dims[i]);
            bytes *= dataset[0].dims[i];
        }

        for (const auto& data : dataset)
        {
            if (fwrite(data.data, 1, bytes, fp) < bytes)
            {
                fprintf(stderr, "%s\n", strerror(errno));
            }
        }

        fclose(fp);
        return true;
    }

    std::vector< IdxUbyteData<N-1> > read(const char* file) const noexcept
    {
        std::vector< IdxUbyteData<N-1> > ret(0);

        FILE* fp = fopen(file, "rb");
        if (fp == nullptr)
        {
            fprintf(stderr, "%s\n", strerror(errno));
            return ret;
        }

        uint32_t magic = this->m_read<32>(fp);
        if (magic != MagicNumber)
        {
            fprintf(stderr, "magic number mismatch: 0x%08x != 0x%08x\n", magic, MagicNumber);
            fclose(fp);
            return ret;
        }

        uint32_t dims[N];
        for (size_t i = 0; i < N; i++)
        {
            dims[i] = this->m_read<32>(fp);
            printf("dim %zu: %u\n", i, dims[i]);
        }

        for (uint32_t i = 0; i < dims[0]; i++)
        {
            size_t bytes = 1;
            IdxUbyteData<N-1>& data = ret.emplace_back();
            for (size_t j = 1; j < N; j++)
            {
                data.dims[j-1] = dims[j];
                bytes *= dims[j];
            }

            data.data = new uint8_t[bytes];
            if (fread(data.data, 1, bytes, fp) < bytes)
            {
                fprintf(stderr, "%s\n", strerror(errno));
            }
        }

        fclose(fp);
        return ret;
    }

private:
    constexpr static const uint32_t MagicNumber = 0x00000800 | N;

    // 大端读
    template<size_t bits>
    uint32_t m_read(FILE* fp) const noexcept
    {
        uint32_t ret = 0;
        uint8_t byte = 0;

        for (size_t i = 0; i < bits / 8; i++)
        {
            ret <<= 8;
            if (fread(&byte, 1, 1, fp) < 1)
            {
                fprintf(stderr, "%s\n", strerror(errno));
            }
            ret |= byte;
        }

        return ret;
    }

    // 大端写
    template<size_t bits>
    void m_write(FILE* fp, uintmax_t value) const noexcept
    {
        constexpr const size_t bytes = bits / 8;
        uint8_t byte = 0;

        for (size_t i = 1; i <= bytes; i++)
        {
            byte = static_cast<uint8_t>(value >> (8 * (bytes - i)));
            fwrite(&byte, 1, 1, fp);
        }
    }
};

#endif // IDX_UBYTE_HPP
一些常见的延迟级别
2024-12-18 21:18:31+08:00

一些常见的延迟级别

亚纳秒级

  • CPU 访问寄存器
  • CPU 时钟周期

纳秒级

  • 访问 L1/L2 缓存
  • 分支预测错误惩罚

十纳秒级

  • 访问 L3 缓存

百纳秒级

  • 系统调用(仅调用本身的开销,不包含执行过程的开销)
  • 计算 64 位数字的 MD5 值

微秒级

  • 线程上下文切换(仅切换本身的开销,不包含切换可能引发的内存换页等开销)
  • 64KB 内存页拷贝

十微秒级

  • Nginx 处理一个 HTTP 请求
  • SSD 读取一个 8KB 页

百微秒级

  • SSD 写入一个 8KB 页
  • 云服务域内网络往返
  • 一次 Redis 读取

毫秒级

  • 云服务域间网络往返
  • 机械硬盘寻道

十毫秒级

  • 美国西海岸到东海岸的网络延迟
  • 内存顺序读取 1GB

百毫秒级

  • bcrypt 加密一个常规长度的密码
  • TLS 握手
  • 中国到美国的网络延迟
    • SSD 顺序读取 1GB

秒级

  • 云服务域内传输 1GB 数据
数字高程模型数据下载
2024-12-18 21:18:31+08:00

数字高程模型数据下载

SRTM

下载地址: https://srtm.csi.cgiar.org/srtmdata/

Mars MGS MOLA DEM 463m v2

Mars MGS MOLA DEM 463m v2 是 NASA 发布的火星数字高程模型数据。

下载地址: https://astrogeology.usgs.gov/search/map/Mars/GlobalSurveyor/MOLA/Mars_MGS_MOLA_DEM_mosaic_global_463m

ETOPO(1.8千米)

ETOPO是一种地形高程数据,由NGDC美国地球物理中心发布,与大多数高程数据不同的是,它还包含海底地形数据。

下载地址: https://www.ncei.noaa.gov/products/etopo-global-relief-model

SRTM15(450米)

SRTM15的空间分辨率为 15 弧秒,精度相当于 0.5km左右,包含了陆地高程和海洋深度数据。

下载地址: https://topex.ucsd.edu/WWW_html/srtm15_plus.html

GMTED(250米)

来自美国地质勘探局USGS和美国国家地理空间情报局NGA,它是对USGS的GTOPO30的进一步优化和发展,对应的最高精度在250米左右。

下载地址: https://www.usgs.gov/coastal-changes-and-impacts/gmted2010

SRTM3(90米)

SRTM,全称为Shuttle Radar Topography Mission,该项目获取了北纬60度至南纬60度之间的雷达影像数据,SRTM3,即精度为3弧秒,即90m一个点,包括非洲、北美、南美、欧亚、澳大利亚以及部分岛屿。

下载地址: http://www.webgis.com/srtm3.html

ASTER GDEM(30米)

全称Advanced Spaceborne Thermal Emission and Reflection Radiometer Global Digital Elevation Model ,即先进星载热发射和反射辐射仪全球数字高程模型,其数据覆盖范围为北纬83°到南纬83°之间的所有陆地区域,达到了地球陆地表面的99%,所以应用十分的广泛。目前比较常用的是 ASTER GDEM V2,另外ASTER GDEM V3也比较常见,质量比V2版本要好的多。

ALOS(12.5米)

ALOS DEM是ALOS(Advanced Land Observing Satellite卫星相控阵型L波段合成孔径雷达(PALSAR)采集,该数据水平及垂直精度可达12.5米。

软件激活功能的实现方法
2024-12-18 21:18:31+08:00

软件激活功能的实现方法

常用激活方式

完全离线激活

通过某种算法规则生成激活码,软件通过该规则对激活码进行验证。用户只要将激活码填入软件即可。

规则示例:

  • 激活码由四个两数组成 AA-BB-CC-DD
  • 规则为 DD = (AA + BB + CC) % 100

这种方法使用起来最方便,但安全性极差,一个激活码即可无限激活。

部分离线激活

设备本身离线,但激活时需要其他联网设备复制进行,激活后使用软件不需要联网。

步骤:

  • 软件获取设备 ID
  • 用户通过其他联网环境再注册网页上提交设备 ID
  • 注册服务器根据设备ID,以某种算法规则生成注册码返回给用户
  • 用户在软件上填入该激活码激活
  • 软件以该规则进行验证

规则示例:

  • 激活码 = SHA256(设备ID)

这种方法使用起来也较为方便,且一个激活码只能用于一台设备。但也有算法被破解的风险。

在线激活

设备全程联网验证。这种方式最安全,但对用户而言十分麻烦。

获取唯一设备 ID

X86 汇编中有一条 cpuid 指令,可以获取 CPU 的 ID,原本包含 CPU 的序列号,但出于隐私保护的原因被取消了,现在 CPU ID 仅含 CPU 型号,而不包含序列号。

可以采用 硬盘序列号 作为设备ID,获取方法如下:

Linux 上获取硬盘序列号

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <linux/limits.h>
#include <libmount/libmount.h>
#include <libudev.h>

#define SERIAL_MAX_LENGTH 1024

// 通过 libmount 获取挂载点的硬盘设备路径
const char* device_path_of_mount_point(const char* mount_point)
{
    static char device_path[PATH_MAX];
    device_path[0] = 0;

    struct libmnt_context* ctx = mnt_new_context();
    if (ctx == NULL)
        return NULL;

    struct libmnt_table* table = NULL;

    if (mnt_context_get_fstab(ctx, &table) < 0)
    {
        mnt_free_context(ctx);
        return NULL;
    }

    struct libmnt_fs* fs = mnt_table_find_target(table, mount_point, MNT_ITER_BACKWARD);
    if (fs == NULL)
    {
        mnt_free_context(ctx);
        return NULL;
    }

    const char* path = mnt_fs_get_srcpath(fs);
    strncpy(device_path, path, PATH_MAX);
    mnt_free_context(ctx);
    return device_path;
}


// 通过 libudev 获取设备路径的序列号
const char* serial_of_device(const char* device_path)
{
    static char device_serial[SERIAL_MAX_LENGTH];
    device_serial[0] = 0;

    struct udev* udev = udev_new();
    if (udev == NULL)
        return NULL;

    struct udev_enumerate* enumerate = udev_enumerate_new(udev);
    if (enumerate == NULL)
    {
        udev_unref(udev);
        return NULL;
    }

    if (udev_enumerate_scan_devices(enumerate) < 0)
    {
        udev_enumerate_unref(enumerate);
        udev_unref(udev);
        return NULL;
    }

    struct udev_list_entry* device_entry = udev_enumerate_get_list_entry(enumerate);
    udev_list_entry_foreach(device_entry, device_entry)
    {
        const char* syspath = udev_list_entry_get_name(device_entry);
        struct udev_device* device = udev_device_new_from_syspath(udev, syspath);
        struct udev_list_entry* link_entry = udev_device_get_devlinks_list_entry(device);

        udev_list_entry_foreach(link_entry, link_entry)
        {
            const char* link = udev_list_entry_get_name(link_entry);
            if (strcmp(link, device_path) == 0)
            {
                const char* serial = udev_device_get_property_value(device, "ID_SERIAL");
                strncpy(device_serial, serial, SERIAL_MAX_LENGTH);
                udev_device_unref(device);
                goto SUCCESS;
            }
        }

        udev_device_unref(device);
    }

SUCCESS:
    udev_enumerate_unref(enumerate);
    udev_unref(udev);
    return device_serial;
}

int main (int argc, char *argv[]) 
{
    const char* device_path = device_path_of_mount_point("/");
    if (device_path == NULL)
        return EXIT_FAILURE;
    
    const char* device_serial = serial_of_device(device_path);
    printf("Path: %s \nSerial: %s\n", device_path, device_serial);
    return EXIT_SUCCESS;
}

Windows 上获取硬盘序列号

#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>

#define SERIAL_MAX_LENGTH 1024

const char* serial_of_device(const char* device_path)
{
    static char device_serial[SERIAL_MAX_LENGTH];
    device_serial[0] = 0;

    HANDLE device = CreateFileA(device_path, 0, 0, NULL, OPEN_EXISTING, 0, NULL);
    if (device == INVALID_HANDLE_VALUE)
        return NULL;

    STORAGE_PROPERTY_QUERY query;
    query.PropertyId = StorageDeviceProperty;
    query.QueryType  = PropertyStandardQuery;

    // 查询存储器头
    STORAGE_DESCRIPTOR_HEADER header;
    BOOL success = DeviceIoControl(device, IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(query), &header, sizeof(header), NULL, NULL);
    if (!success || header.Size == 0)
    {
        CloseHandle(device);
        return NULL;
    }

    // 查询存储器全部属性
    void* buffer = malloc(header.Size);
    if (buffer == NULL)
    {
        CloseHandle(device);
        return NULL;
    }
    success = DeviceIoControl(device, IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(query), buffer, header.Size, NULL, NULL);
    if (!success)
    {
        free(buffer);
        CloseHandle(device);
        return NULL;
    }

    // 获取序列号的偏移并读取序列号
    const STORAGE_DEVICE_DESCRIPTOR* descriptor = (STORAGE_DEVICE_DESCRIPTOR*)buffer;
    strncpy(device_serial, (char*)buffer + descriptor->SerialNumberOffset, SERIAL_MAX_LENGTH);

    free(buffer);
    CloseHandle(device);
    return device_serial;
}

int main()
{
    const char* device_serial = serial_of_device("\\\\.\\C:");
    if (device_serial == NULL)
        return EXIT_FAILURE;

    printf("%s\n", device_serial);
    return EXIT_SUCCESS;
}
INSTRUCTION
2024-12-18 18:52:58+08:00

Phosphophyllite

SEO 友好的纯静态 Markdown 博客系统

SEO-friendly pure static Markdown blog system

Preview

Usage - 使用说明

存储结构:

  • blog/article 用于存储文章
  • blog/resource 用户存储资源文件,例如图片

Storage Structure:

  • blog/article is used to store articles
  • blog/resource is used to store resource files such as images

Build & Deploy - 构建与部署

python main.py