mirror of
https://github.com/oltaco/Adafruit_nRF52_Bootloader_OTAFIX.git
synced 2026-10-24 12:54:08 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5b654eb5e | ||
|
|
60debfc217 | ||
|
|
2713069643 | ||
|
|
8df909f0a8 | ||
|
|
1d816c5cf3 | ||
|
|
66d1770407 | ||
|
|
8ab4774c93 | ||
|
|
1b54f0457d | ||
|
|
a004b7619e | ||
|
|
04fc396fd8 | ||
|
|
04e0df1c18 | ||
|
|
6d58633db0 | ||
|
|
15ca75d10d | ||
|
|
174e6e9724 | ||
|
|
53a99195bf | ||
|
|
8def8aee53 | ||
|
|
c51a227fd9 | ||
|
|
f3e3cefb23 | ||
|
|
73456ac80e | ||
|
|
113195eabd | ||
|
|
0eeff3e679 | ||
|
|
be5a925f72 | ||
|
|
ae28f5b425 | ||
|
|
b8a0551678 | ||
|
|
0f216b8618 | ||
|
|
52cfdaa2d2 | ||
|
|
a258c6d988 | ||
|
|
e0251415e7 | ||
|
|
12bb62eadd | ||
|
|
e2afc36dae | ||
|
|
4ea74d4600 | ||
|
|
9bc01fb061 | ||
|
|
01492634b6 | ||
|
|
05726e2575 |
No files matched your search
@@ -54,3 +54,5 @@ dkms.conf
|
||||
src/segger/Output
|
||||
_build-*/
|
||||
bin/
|
||||
*.emSession
|
||||
*.jlink
|
||||
@@ -251,6 +251,8 @@ CFLAGS += -ffunction-sections -fdata-sections -fno-strict-aliasing
|
||||
CFLAGS += -fno-builtin --short-enums -fstack-usage
|
||||
|
||||
# Defined Symbol (MACROS)
|
||||
|
||||
# TODO use GIT_VERSION (numberic format)
|
||||
CFLAGS += -DMK_BOOTLOADER_VERSION=0x0$(SD_VER1)0$(SD_VER2)0$(SD_VER3)UL
|
||||
|
||||
CFLAGS += -D__HEAP_SIZE=0
|
||||
|
||||
@@ -9,6 +9,7 @@ This is a CDC/DFU/UF2 bootloader for nRF52 boards.
|
||||
- Particle Argon
|
||||
- Particle Boron
|
||||
- Particle Xenon
|
||||
- MakerDiary MDK nRF52840 USB Dongle
|
||||
|
||||
UF2 is an easy-to-use bootloader that appears as a flash drive. You can just copy `.uf2`-format
|
||||
application images to the flash drive to load new firmware.
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# Adafruit nRF52 Bootloader Changelog
|
||||
|
||||
## 0.2.10
|
||||
|
||||
- Fixed various FAT issues, thanks to @henrygab
|
||||
- Added MakerDiary MDK nrf52840 USB dongle support, thanks to @gpshead
|
||||
- Fixed incorrect button mapping for Feather nRF52840
|
||||
|
||||
## 0.2.9
|
||||
|
||||
- Fixed incomplete writes on Windows. Upated tinyusb to handle write10 completion, and use it for finalizing dfu process. Will prevent windows error message pop up. This will also give an extra time for flash writing to complete.
|
||||
|
||||
@@ -53,8 +53,8 @@
|
||||
/* BUTTON
|
||||
*------------------------------------------------------------------*/
|
||||
#define BUTTONS_NUMBER 2
|
||||
#define BUTTON_1 _PINNUM(0, 15)
|
||||
#define BUTTON_2 _PINNUM(0, 19)
|
||||
#define BUTTON_1 _PINNUM(1, 02)
|
||||
#define BUTTON_2 _PINNUM(0, 10)
|
||||
#define BUTTON_PULL NRF_GPIO_PIN_PULLUP
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/**************************************************************************/
|
||||
/*!
|
||||
@file mdk_nrf52840_dongle.h
|
||||
@author gpshead (krypto.org)
|
||||
|
||||
@section LICENSE
|
||||
|
||||
Software License Agreement (BSD License)
|
||||
|
||||
Copyright (c) 2019, Adafruit Industries (adafruit.com)
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. Neither the name of the copyright holders nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef _MDK_NRF52840_DONGLE_H_
|
||||
#define _MDK_NRF52840_DONGLE_H_
|
||||
|
||||
#define _PINNUM(port, pin) ((port)*32 + (pin))
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* LED
|
||||
*------------------------------------------------------------------*/
|
||||
#define LEDS_NUMBER 2 // TODO(gpshead): support 0.
|
||||
#define LED_PRIMARY_PIN _PINNUM(0, 23) // Red
|
||||
#define LED_SECONDARY_PIN _PINNUM(0, 24) // Blue
|
||||
#define LED_STATE_ON 0
|
||||
|
||||
//#define LED_RGB_RED_PIN _PINNUM(0, 23)
|
||||
//#define LED_RGB_GREEN_PIN _PINNUM(0, 22)
|
||||
//#define LED_RGB_BLUE_PIN _PINNUM(0, 24)
|
||||
#define BOARD_RGB_BRIGHTNESS 0x404040
|
||||
/*------------------------------------------------------------------*/
|
||||
/* BUTTON
|
||||
*------------------------------------------------------------------*/
|
||||
// TODO(gpshead): simplify, have code support 0. double reset only.
|
||||
#define BUTTONS_NUMBER 2 // none connected at all
|
||||
#define BUTTON_1 _PINNUM(0, 18) // unusable: RESET
|
||||
#define BUTTON_2 _PINNUM(0, 19) // no connection
|
||||
#define BUTTON_PULL NRF_GPIO_PIN_PULLUP
|
||||
|
||||
// Used as model string in OTA mode
|
||||
#define DIS_MANUFACTURER "MakerDiary"
|
||||
#define DIS_MODEL "nRF52840 Micro Dev Kit USB Dongle"
|
||||
|
||||
#define PRODUCT_NAME "MDK nRF52840 USB Dongle"
|
||||
#define VOLUME_LABEL "MDK840DONGL"
|
||||
|
||||
#define BOARD_ID "MakerDiary-nRF52840-USB-Dongle"
|
||||
|
||||
#define INDEX_URL "https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle/"
|
||||
|
||||
#endif /* _MDK_NRF52840_DONGLE_H_ */
|
||||
@@ -63,6 +63,7 @@
|
||||
<file file_name="../usb/uf2/uf2cfg.h" />
|
||||
<file file_name="../usb/uf2/uf2.h" />
|
||||
<file file_name="../usb/uf2/ghostfat.c" />
|
||||
<file file_name="../usb/uf2/macros.h" />
|
||||
</folder>
|
||||
<file file_name="../usb/msc_uf2.c" />
|
||||
<file file_name="../usb/usb.c" />
|
||||
|
||||
+4
-2
@@ -63,7 +63,8 @@ int write_block(uint32_t block_no, uint8_t *data, bool quiet, WriteState *state)
|
||||
int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize)
|
||||
{
|
||||
void const* response = NULL;
|
||||
uint16_t resplen = 0;
|
||||
int32_t resplen = 0;
|
||||
memset(buffer, 0, bufsize);
|
||||
|
||||
switch ( scsi_cmd[0] )
|
||||
{
|
||||
@@ -98,7 +99,7 @@ int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer,
|
||||
}
|
||||
|
||||
// return len must not larger than bufsize
|
||||
if ( resplen > bufsize ) resplen = bufsize;
|
||||
if ( resplen > (int32_t)bufsize ) resplen = bufsize;
|
||||
|
||||
// copy response to stack's buffer if any
|
||||
if ( response && resplen )
|
||||
@@ -114,6 +115,7 @@ int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer,
|
||||
int32_t tud_msc_read10_cb (uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize)
|
||||
{
|
||||
(void) lun;
|
||||
memset(buffer, 0, bufsize);
|
||||
|
||||
// since we return block size each, offset should always be zero
|
||||
TU_ASSERT(offset == 0, -1);
|
||||
|
||||
+79
-30
@@ -1,4 +1,6 @@
|
||||
|
||||
#include "macros.h"
|
||||
|
||||
#include "uf2.h"
|
||||
#include "flash_nrf5x.h"
|
||||
#include <string.h>
|
||||
@@ -47,7 +49,6 @@ typedef struct {
|
||||
uint16_t startCluster;
|
||||
uint32_t size;
|
||||
} __attribute__((packed)) DirEntry;
|
||||
|
||||
STATIC_ASSERT(sizeof(DirEntry) == 32);
|
||||
|
||||
struct TextFile {
|
||||
@@ -59,6 +60,7 @@ struct TextFile {
|
||||
|
||||
#define STR0(x) #x
|
||||
#define STR(x) STR0(x)
|
||||
|
||||
const char infoUf2File[] = //
|
||||
"UF2 Bootloader " UF2_VERSION "\r\n"
|
||||
"Model: " PRODUCT_NAME "\r\n"
|
||||
@@ -76,16 +78,27 @@ const char indexFile[] = //
|
||||
"</body>"
|
||||
"</html>\n";
|
||||
|
||||
// WARNING -- code presumes only one NULL .content for .UF2 file
|
||||
// and requires it be the last element of the array
|
||||
static struct TextFile const info[] = {
|
||||
{.name = "INFO_UF2TXT", .content = infoUf2File},
|
||||
{.name = "INDEX HTM", .content = indexFile},
|
||||
{.name = "CURRENT UF2"},
|
||||
};
|
||||
#define NUM_INFO (sizeof(info) / sizeof(info[0]))
|
||||
|
||||
// WARNING -- code presumes each non-UF2 file content fits in single sector
|
||||
// Cannot programmatically statically assert .content length
|
||||
// for each element above.
|
||||
STATIC_ASSERT(ARRAY_SIZE2(indexFile) < 512);
|
||||
|
||||
|
||||
#define NUM_FILES (ARRAY_SIZE2(info))
|
||||
#define NUM_DIRENTRIES (NUM_FILES + 1) // Code adds volume label as first root directory entry
|
||||
|
||||
|
||||
#define UF2_SIZE (current_flash_size() * 2)
|
||||
#define UF2_SECTORS (UF2_SIZE / 512)
|
||||
#define UF2_FIRST_SECTOR (NUM_INFO + 1)
|
||||
#define UF2_FIRST_SECTOR (NUM_FILES + 1) // WARNING -- code presumes each non-UF2 file content fits in single sector
|
||||
#define UF2_LAST_SECTOR (UF2_FIRST_SECTOR + UF2_SECTORS - 1)
|
||||
|
||||
#define RESERVED_SECTORS 1
|
||||
@@ -97,6 +110,13 @@ static struct TextFile const info[] = {
|
||||
#define START_ROOTDIR (START_FAT1 + SECTORS_PER_FAT)
|
||||
#define START_CLUSTERS (START_ROOTDIR + ROOT_DIR_SECTORS)
|
||||
|
||||
// all directory entries must fit in a single sector
|
||||
// because otherwise current code overflows buffer
|
||||
#define DIRENTRIES_PER_SECTOR (512/sizeof(DirEntry))
|
||||
|
||||
STATIC_ASSERT(NUM_DIRENTRIES < DIRENTRIES_PER_SECTOR * ROOT_DIR_SECTORS);
|
||||
|
||||
|
||||
static FAT_BootBlock const BootBlock = {
|
||||
.JumpInstruction = {0xeb, 0x3c, 0x90},
|
||||
.OEMInfo = "UF2 UF2 ",
|
||||
@@ -104,12 +124,13 @@ static FAT_BootBlock const BootBlock = {
|
||||
.SectorsPerCluster = 1,
|
||||
.ReservedSectors = RESERVED_SECTORS,
|
||||
.FATCopies = 2,
|
||||
.RootDirectoryEntries = (ROOT_DIR_SECTORS * 512 / 32),
|
||||
.RootDirectoryEntries = (ROOT_DIR_SECTORS * DIRENTRIES_PER_SECTOR),
|
||||
.TotalSectors16 = NUM_FAT_BLOCKS - 2,
|
||||
.MediaDescriptor = 0xF8,
|
||||
.SectorsPerFAT = SECTORS_PER_FAT,
|
||||
.SectorsPerTrack = 1,
|
||||
.Heads = 1,
|
||||
.PhysicalDriveNum = 0x80, // to match MediaDescriptor of 0xF8
|
||||
.ExtendedBootSig = 0x29,
|
||||
.VolumeSerialNumber = 0x00420042,
|
||||
.VolumeLabel = VOLUME_LABEL,
|
||||
@@ -123,35 +144,37 @@ static FAT_BootBlock const BootBlock = {
|
||||
static uint32_t current_flash_size(void)
|
||||
{
|
||||
static uint32_t flash_sz = 0;
|
||||
uint32_t result = flash_sz; // presumes atomic 32-bit read/write and static result
|
||||
|
||||
// only need to compute once
|
||||
if ( flash_sz == 0 )
|
||||
if ( result == 0 )
|
||||
{
|
||||
// return 1 block of 256 bytes
|
||||
if ( !bootloader_app_is_valid(DFU_BANK_0_REGION_START) )
|
||||
{
|
||||
flash_sz = 256;
|
||||
result = 256;
|
||||
}else
|
||||
{
|
||||
bootloader_settings_t const * boot_setting;
|
||||
bootloader_util_settings_get(&boot_setting);
|
||||
|
||||
flash_sz = boot_setting->bank_0_size;
|
||||
result = boot_setting->bank_0_size;
|
||||
|
||||
// Copy size must be multiple of 256 bytes
|
||||
// else we will got an issue copying current.uf2
|
||||
if (flash_sz & 0xff)
|
||||
if (result & 0xff)
|
||||
{
|
||||
flash_sz = (flash_sz & ~0xff) + 256;
|
||||
result = (result & ~0xff) + 256;
|
||||
}
|
||||
|
||||
// if bank0 size is not valid, happens when flashed with jlink
|
||||
// use maximum application size
|
||||
if ( (flash_sz == 0) || (flash_sz == 0xFFFFFFFFUL) )
|
||||
if ( (result == 0) || (result == 0xFFFFFFFFUL) )
|
||||
{
|
||||
flash_sz = FLASH_SIZE;
|
||||
result = FLASH_SIZE;
|
||||
}
|
||||
}
|
||||
flash_sz = result; // presumes atomic 32-bit read/write and static result
|
||||
}
|
||||
|
||||
return flash_sz;
|
||||
@@ -176,47 +199,73 @@ void read_block(uint32_t block_no, uint8_t *data) {
|
||||
memset(data, 0, 512);
|
||||
uint32_t sectionIdx = block_no;
|
||||
|
||||
if (block_no == 0) {
|
||||
if (block_no == 0) { // Requested boot block
|
||||
memcpy(data, &BootBlock, sizeof(BootBlock));
|
||||
data[510] = 0x55;
|
||||
data[511] = 0xaa;
|
||||
// logval("data[0]", data[0]);
|
||||
} else if (block_no < START_ROOTDIR) {
|
||||
} else if (block_no < START_ROOTDIR) { // Requested FAT table sector
|
||||
sectionIdx -= START_FAT0;
|
||||
// logval("sidx", sectionIdx);
|
||||
if (sectionIdx >= SECTORS_PER_FAT)
|
||||
sectionIdx -= SECTORS_PER_FAT;
|
||||
sectionIdx -= SECTORS_PER_FAT; // second FAT is same as the first...
|
||||
if (sectionIdx == 0) {
|
||||
data[0] = 0xf0;
|
||||
for (int i = 1; i < NUM_INFO * 2 + 4; ++i) {
|
||||
data[0] = 0xf8; // first FAT entry must match BPB MediaDescriptor
|
||||
// WARNING -- code presumes only one NULL .content for .UF2 file
|
||||
// and all non-NULL .content fit in one sector
|
||||
// and requires it be the last element of the array
|
||||
for (int i = 1; i < NUM_FILES * 2 + 4; ++i) {
|
||||
data[i] = 0xff;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
for (int i = 0; i < 256; ++i) { // Generate the FAT chain for the firmware "file"
|
||||
uint32_t v = sectionIdx * 256 + i;
|
||||
if (UF2_FIRST_SECTOR <= v && v <= UF2_LAST_SECTOR)
|
||||
((uint16_t *)(void *)data)[i] = v == UF2_LAST_SECTOR ? 0xffff : v + 1;
|
||||
}
|
||||
} else if (block_no < START_CLUSTERS) {
|
||||
} else if (block_no < START_CLUSTERS) { // Requested root directory sector
|
||||
|
||||
sectionIdx -= START_ROOTDIR;
|
||||
if (sectionIdx == 0) {
|
||||
DirEntry *d = (void *)data;
|
||||
|
||||
DirEntry *d = (void *)data;
|
||||
int remainingEntries = DIRENTRIES_PER_SECTOR;
|
||||
if (sectionIdx == 0) { // volume label first
|
||||
// volume label is first directory entry
|
||||
padded_memcpy(d->name, (char const *) BootBlock.VolumeLabel, 11);
|
||||
d->attrs = 0x28;
|
||||
for (int i = 0; i < NUM_INFO; ++i) {
|
||||
d++;
|
||||
struct TextFile const *inf = &info[i];
|
||||
d->size = inf->content ? strlen(inf->content) : UF2_SIZE;
|
||||
d->startCluster = i + 2;
|
||||
padded_memcpy(d->name, inf->name, 11);
|
||||
}
|
||||
d++;
|
||||
remainingEntries--;
|
||||
}
|
||||
|
||||
for (int i = DIRENTRIES_PER_SECTOR * sectionIdx;
|
||||
remainingEntries > 0 && i < NUM_FILES;
|
||||
i++, d++) {
|
||||
|
||||
// WARNING -- code presumes all but last file take exactly one sector
|
||||
uint16_t startCluster = i + 2;
|
||||
|
||||
struct TextFile const * inf = &info[i];
|
||||
padded_memcpy(d->name, inf->name, 11);
|
||||
d->createTimeFine = __SECONDS_INT__ % 2 * 100;
|
||||
d->createTime = __DOSTIME__;
|
||||
d->createDate = __DOSDATE__;
|
||||
d->lastAccessDate = __DOSDATE__;
|
||||
d->highStartCluster = startCluster >> 8;
|
||||
// DIR_WrtTime and DIR_WrtDate must be supported
|
||||
d->updateTime = __DOSTIME__;
|
||||
d->updateDate = __DOSDATE__;
|
||||
d->startCluster = startCluster & 0xFF;
|
||||
// WARNING -- code presumes only one NULL .content for .UF2 file
|
||||
// and requires it be the last element of the array
|
||||
d->size = inf->content ? strlen(inf->content) : UF2_SIZE;
|
||||
}
|
||||
|
||||
} else {
|
||||
sectionIdx -= START_CLUSTERS;
|
||||
if (sectionIdx < NUM_INFO - 1) {
|
||||
if (sectionIdx < NUM_FILES - 1) {
|
||||
memcpy(data, info[sectionIdx].content, strlen(info[sectionIdx].content));
|
||||
} else {
|
||||
sectionIdx -= NUM_INFO - 1;
|
||||
} else { // generate the UF2 file data on-the-fly
|
||||
sectionIdx -= NUM_FILES - 1;
|
||||
uint32_t addr = USER_FLASH_START + sectionIdx * 256;
|
||||
if (addr < USER_FLASH_START+FLASH_SIZE) {
|
||||
UF2_Block *bl = (void *)data;
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
/**
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c)
|
||||
|
||||
All rights reserved.
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef ARRAYSIZE2_H
|
||||
#define ARRAYSIZE2_H
|
||||
|
||||
#ifndef __has_feature
|
||||
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 199711L
|
||||
#pragma message "using Ivan J. Johnson's ARRAY_SIZE2"
|
||||
|
||||
// Works on older compilers, even Visual C++ 6....
|
||||
// Created by Ivan J. Johnson, March 06, 2007
|
||||
// See http://drdobbs.com/cpp/197800525?pgno=1
|
||||
//
|
||||
// Pseudocode:
|
||||
// if x is not an array
|
||||
// issue a compile-time error
|
||||
// else
|
||||
// use the traditional (non-typesafe) C99 COUNTOF expression
|
||||
//
|
||||
// If the argument is any of:
|
||||
// object of class type, such as an std::vector
|
||||
// floating-point type
|
||||
// function pointer
|
||||
// pointer-to-member
|
||||
// then the first reinterpret_cast<> is not legal (compiler error)
|
||||
//
|
||||
// The type for check1 is chosen and named to help understand
|
||||
// the cause of the error, because the class name is likely to
|
||||
// appear in the compiler error message.
|
||||
//
|
||||
// If check1 succeeds, then the argument must be one of:
|
||||
// an integral type
|
||||
// an enumerated type
|
||||
// a pointer to an object
|
||||
// an array
|
||||
//
|
||||
// Check2 expands approximately to sizeof(check_type(x, &x)),
|
||||
// where check_type is an overloaded function.
|
||||
// Because this is purely a compile-time computation,
|
||||
// the function is never really called or even implemented,
|
||||
// but it lets the compiler apply overload resolution,
|
||||
// which allows further type discrimination.
|
||||
// There are three possibilities to consider:
|
||||
// x is an integral type or enumerated type.
|
||||
// In this case, neither of the two function overloads
|
||||
// is a match, resulting in a compiler error.
|
||||
// x is a pointer to an object.
|
||||
// In this case, the first argument to check_type()
|
||||
// is a pointer and the second one is a pointer-to-pointer.
|
||||
// The best function match is the first overload of check_type,
|
||||
// the one that returns an incomplete type (Is_pointer).
|
||||
// However, because Is_pointer is an incomplete type,
|
||||
// sizeof(Is_pointer) is not a valid expression,
|
||||
// resulting in a compiler error.
|
||||
// x is an array.
|
||||
// In this case, the first argument to check_type()
|
||||
// is an array and the second is a pointer-to-array.
|
||||
// A pointer-to-array is *NOT* convertible to a
|
||||
// pointer-to-pointer, so the first overload of
|
||||
// check_type() is not a match.
|
||||
// However, an array IS convertible to a pointer,
|
||||
// and a pointer-to-array already is a pointer.
|
||||
// Any pointer is convertible to a void*,
|
||||
// so the second function overload is a match.
|
||||
// That overload returns a complete type (Is_array).
|
||||
// Because it's a complete type,
|
||||
// sizeof(Is_array) is a valid expression.
|
||||
// Thus, the compiler has EXCLUDED every possible type
|
||||
// except arrays via compilation errors before reaching
|
||||
// the third line.
|
||||
// Moreover, check1 and check2 are reduced to the value zero,
|
||||
// while the third line is the old type-unsafe C-style macro,
|
||||
// now made entirely type-safe.
|
||||
//
|
||||
// Additional benefits:
|
||||
// The result is itself constexpr
|
||||
//
|
||||
//
|
||||
#define ARRAY_SIZE2(arr) ( \
|
||||
0 * sizeof(reinterpret_cast<const ::Bad_arg_to_COUNTOF*>(arr)) + /*check1*/ \
|
||||
0 * sizeof(::Bad_arg_to_COUNTOF::check_type((arr), &(arr))) + /*check2*/ \
|
||||
sizeof(arr) / sizeof((arr)[0]) /* eval */ \
|
||||
)
|
||||
|
||||
struct Bad_arg_to_COUNTOF {
|
||||
class Is_pointer; // incomplete
|
||||
class Is_array {};
|
||||
template <typename T>
|
||||
static Is_pointer check_type(const T*, const T* const*);
|
||||
static Is_array check_type(const void*, const void*);
|
||||
};
|
||||
|
||||
#elif __cplusplus >= 201103L || /* any compiler claiming C++11 support */ \
|
||||
_MSC_VER >= 1900 || /* Visual C++ 2015 or higher */ \
|
||||
__has_feature(cxx_constexpr) /* CLang versions supporting constexp */
|
||||
|
||||
#pragma message "C++11 version ARRAY_SIZE2"
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <typename T, std::size_t N>
|
||||
constexpr std::size_t countof(T const (&)[N]) noexcept
|
||||
{
|
||||
return N;
|
||||
}
|
||||
} // namespace detail
|
||||
#define ARRAY_SIZE2(arr) detail::countof(arr)
|
||||
|
||||
#elif _MSC_VER // Visual C++ fallback
|
||||
|
||||
#pragma message "using Microsoft Visual C++ intrinsic ARRAY_SIZE2"
|
||||
#define ARRAY_SIZE2(arr) _countof(arr)
|
||||
|
||||
#elif __cplusplus >= 199711L && ( /* C++ 98 trick */ \
|
||||
defined(__INTEL_COMPILER) || \
|
||||
defined(__clang__) || \
|
||||
(defined(__GNUC__) && ( \
|
||||
(__GNUC__ > 4) || \
|
||||
(__GNUC__ == 4 && __GNUC_MINOR__ >= 4) \
|
||||
)))
|
||||
|
||||
#pragma message "C++98 version ARRAY_SIZE2"
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
char(&_ArraySizeHelperRequiresArray(T(&)[N]))[N];
|
||||
#define ARRAY_SIZE2(x) sizeof(_ArraySizeHelperRequiresArray(x))
|
||||
|
||||
#else
|
||||
|
||||
#pragma message "Using type-unsafe version of ARRAY_SIZE2"
|
||||
// This is the worst-case scenario macro.
|
||||
// While it is valid C, it is NOT typesafe.
|
||||
// For example, if the parameter arr is a pointer instead of array,
|
||||
// the compiler will SILENTLY give a (likely) incorrect result.
|
||||
#define ARRAY_SIZE2(arr) sizeof(arr) / sizeof(arr[0])
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif // ARRAYSIZE2_H
|
||||
|
||||
|
||||
#ifndef COMPILE_DATE_H
|
||||
#define COMPILE_DATE_H
|
||||
|
||||
#define __YEAR_INT__ ((( \
|
||||
(__DATE__ [ 7u] - '0') * 10u + \
|
||||
(__DATE__ [ 8u] - '0')) * 10u + \
|
||||
(__DATE__ [ 9u] - '0')) * 10u + \
|
||||
(__DATE__ [10u] - '0'))
|
||||
|
||||
#define __MONTH_INT__ ( \
|
||||
(__DATE__ [2u] == 'n' && __DATE__ [1u] == 'a') ? 1u /*Jan*/ \
|
||||
: (__DATE__ [2u] == 'b' ) ? 2u /*Feb*/ \
|
||||
: (__DATE__ [2u] == 'r' && __DATE__ [1u] == 'a') ? 3u /*Mar*/ \
|
||||
: (__DATE__ [2u] == 'r' ) ? 4u /*Apr*/ \
|
||||
: (__DATE__ [2u] == 'y' ) ? 5u /*May*/ \
|
||||
: (__DATE__ [2u] == 'n' ) ? 6u /*Jun*/ \
|
||||
: (__DATE__ [2u] == 'l' ) ? 7u /*Jul*/ \
|
||||
: (__DATE__ [2u] == 'g' ) ? 8u /*Jul*/ \
|
||||
: (__DATE__ [2u] == 'p' ) ? 9u /*Jul*/ \
|
||||
: (__DATE__ [2u] == 't' ) ? 10u /*Jul*/ \
|
||||
: (__DATE__ [2u] == 'v' ) ? 11u /*Jul*/ \
|
||||
: 12u /*Dec*/ )
|
||||
|
||||
#define __DAY_INT__ ( \
|
||||
(__DATE__ [4u] == ' ' ? 0u : __DATE__ [4u] - '0') * 10u \
|
||||
+ (__DATE__ [5u] - '0') )
|
||||
|
||||
// __TIME__ expands to an eight-character string constant
|
||||
// "23:59:01", or (if cannot determine time) "??:??:??"
|
||||
#define __HOUR_INT__ ( \
|
||||
(__TIME__ [0u] == '?' ? 0u : __TIME__ [0u] - '0') * 10u \
|
||||
+ (__TIME__ [1u] == '?' ? 0u : __TIME__ [1u] - '0') )
|
||||
|
||||
#define __MINUTE_INT__ ( \
|
||||
(__TIME__ [3u] == '?' ? 0u : __TIME__ [3u] - '0') * 10u \
|
||||
+ (__TIME__ [4u] == '?' ? 0u : __TIME__ [4u] - '0') )
|
||||
|
||||
#define __SECONDS_INT__ ( \
|
||||
(__TIME__ [6u] == '?' ? 0u : __TIME__ [6u] - '0') * 10u \
|
||||
+ (__TIME__ [7u] == '?' ? 0u : __TIME__ [7u] - '0') )
|
||||
|
||||
|
||||
#define __DOSDATE__ ( \
|
||||
((__YEAR_INT__ - 1980u) << 9u) | \
|
||||
( __MONTH_INT__ << 5u) | \
|
||||
( __DAY_INT__ << 0u) )
|
||||
|
||||
#define __DOSTIME__ ( \
|
||||
( __HOUR_INT__ << 11u) | \
|
||||
( __MONTH_INT__ << 5u) | \
|
||||
( __DAY_INT__ << 0u) )
|
||||
|
||||
#endif // COMPILE_DATE_H
|
||||
|
||||
Reference in new issue
Block a user