from bbc microbit library
Dependencies: BLE_API mbed-dev-bin nRF51822
Fork of microbit-dal by
inc/drivers/MicroBitStorage.h@35:8ce23bc1af38, 2016-07-13 (annotated)
- Committer:
- LancasterUniversity
- Date:
- Wed Jul 13 12:18:14 2016 +0100
- Revision:
- 35:8ce23bc1af38
- Parent:
- 32:ece16b5987dd
- Child:
- 37:b624ae5e94a5
Synchronized with git rev 732971e7
Author: James Devine
microbit-dal: Added events to MicroBitPin
Added rise, fall, pulse HI and LO events.
The pulse Hi and LO event timestamp given in the MicroBitEvent is the
duration for which the input was HI or LO for.
eventOn(int eventType) is used to configure the events generated
from the pin instance.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
Jonathan Austin |
1:8aa5cdb4ab67 | 1 | /* |
Jonathan Austin |
1:8aa5cdb4ab67 | 2 | The MIT License (MIT) |
Jonathan Austin |
1:8aa5cdb4ab67 | 3 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 4 | Copyright (c) 2016 British Broadcasting Corporation. |
Jonathan Austin |
1:8aa5cdb4ab67 | 5 | This software is provided by Lancaster University by arrangement with the BBC. |
Jonathan Austin |
1:8aa5cdb4ab67 | 6 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 7 | Permission is hereby granted, free of charge, to any person obtaining a |
Jonathan Austin |
1:8aa5cdb4ab67 | 8 | copy of this software and associated documentation files (the "Software"), |
Jonathan Austin |
1:8aa5cdb4ab67 | 9 | to deal in the Software without restriction, including without limitation |
Jonathan Austin |
1:8aa5cdb4ab67 | 10 | the rights to use, copy, modify, merge, publish, distribute, sublicense, |
Jonathan Austin |
1:8aa5cdb4ab67 | 11 | and/or sell copies of the Software, and to permit persons to whom the |
Jonathan Austin |
1:8aa5cdb4ab67 | 12 | Software is furnished to do so, subject to the following conditions: |
Jonathan Austin |
1:8aa5cdb4ab67 | 13 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 14 | The above copyright notice and this permission notice shall be included in |
Jonathan Austin |
1:8aa5cdb4ab67 | 15 | all copies or substantial portions of the Software. |
Jonathan Austin |
1:8aa5cdb4ab67 | 16 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
Jonathan Austin |
1:8aa5cdb4ab67 | 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
Jonathan Austin |
1:8aa5cdb4ab67 | 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
Jonathan Austin |
1:8aa5cdb4ab67 | 20 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
Jonathan Austin |
1:8aa5cdb4ab67 | 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
Jonathan Austin |
1:8aa5cdb4ab67 | 22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
Jonathan Austin |
1:8aa5cdb4ab67 | 23 | DEALINGS IN THE SOFTWARE. |
Jonathan Austin |
1:8aa5cdb4ab67 | 24 | */ |
Jonathan Austin |
1:8aa5cdb4ab67 | 25 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 26 | #ifndef MICROBIT_STORAGE_H |
Jonathan Austin |
1:8aa5cdb4ab67 | 27 | #define MICROBIT_STORAGE_H |
Jonathan Austin |
1:8aa5cdb4ab67 | 28 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 29 | #include "mbed.h" |
Jonathan Austin |
1:8aa5cdb4ab67 | 30 | #include "MicroBitConfig.h" |
Jonathan Austin |
1:8aa5cdb4ab67 | 31 | #include "ManagedString.h" |
Jonathan Austin |
1:8aa5cdb4ab67 | 32 | #include "ErrorNo.h" |
Jonathan Austin |
1:8aa5cdb4ab67 | 33 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 34 | #define MICROBIT_STORAGE_MAGIC 0xCAFE |
Jonathan Austin |
1:8aa5cdb4ab67 | 35 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 36 | #define MICROBIT_STORAGE_BLOCK_SIZE 48 |
Jonathan Austin |
1:8aa5cdb4ab67 | 37 | #define MICROBIT_STORAGE_KEY_SIZE 16 |
Jonathan Austin |
1:8aa5cdb4ab67 | 38 | #define MICROBIT_STORAGE_VALUE_SIZE MICROBIT_STORAGE_BLOCK_SIZE - MICROBIT_STORAGE_KEY_SIZE |
Jonathan Austin |
1:8aa5cdb4ab67 | 39 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 40 | #define MICROBIT_STORAGE_STORE_PAGE_OFFSET 17 //Use the page just above the BLE Bond Data. |
Jonathan Austin |
1:8aa5cdb4ab67 | 41 | #define MICROBIT_STORAGE_SCRATCH_PAGE_OFFSET 19 //Use the page just below the BLE Bond Data. |
Jonathan Austin |
1:8aa5cdb4ab67 | 42 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 43 | struct KeyValuePair |
Jonathan Austin |
1:8aa5cdb4ab67 | 44 | { |
LancasterUniversity | 3:d86a4ddc1867 | 45 | uint8_t key[MICROBIT_STORAGE_KEY_SIZE]; |
LancasterUniversity | 3:d86a4ddc1867 | 46 | uint8_t value[MICROBIT_STORAGE_VALUE_SIZE]; |
Jonathan Austin |
1:8aa5cdb4ab67 | 47 | }; |
Jonathan Austin |
1:8aa5cdb4ab67 | 48 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 49 | struct KeyValueStore |
Jonathan Austin |
1:8aa5cdb4ab67 | 50 | { |
Jonathan Austin |
1:8aa5cdb4ab67 | 51 | uint32_t magic; |
Jonathan Austin |
1:8aa5cdb4ab67 | 52 | uint32_t size; |
Jonathan Austin |
1:8aa5cdb4ab67 | 53 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 54 | KeyValueStore(uint32_t magic, uint32_t size) |
Jonathan Austin |
1:8aa5cdb4ab67 | 55 | { |
Jonathan Austin |
1:8aa5cdb4ab67 | 56 | this->magic = magic; |
Jonathan Austin |
1:8aa5cdb4ab67 | 57 | this->size = size; |
Jonathan Austin |
1:8aa5cdb4ab67 | 58 | } |
Jonathan Austin |
1:8aa5cdb4ab67 | 59 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 60 | KeyValueStore() |
Jonathan Austin |
1:8aa5cdb4ab67 | 61 | { |
Jonathan Austin |
1:8aa5cdb4ab67 | 62 | this->magic = 0; |
Jonathan Austin |
1:8aa5cdb4ab67 | 63 | this->size = 0; |
Jonathan Austin |
1:8aa5cdb4ab67 | 64 | } |
Jonathan Austin |
1:8aa5cdb4ab67 | 65 | }; |
Jonathan Austin |
1:8aa5cdb4ab67 | 66 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 67 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 68 | /** |
Jonathan Austin |
1:8aa5cdb4ab67 | 69 | * Class definition for the MicroBitStorage class. |
Jonathan Austin |
1:8aa5cdb4ab67 | 70 | * This allows reading and writing of small blocks of data to FLASH memory. |
Jonathan Austin |
1:8aa5cdb4ab67 | 71 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 72 | * This class operates as a key value store, it allows the retrieval, addition |
Jonathan Austin |
1:8aa5cdb4ab67 | 73 | * and deletion of KeyValuePairs. |
Jonathan Austin |
1:8aa5cdb4ab67 | 74 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 75 | * The first 8 bytes are reserved for the KeyValueStore struct which gives core |
Jonathan Austin |
1:8aa5cdb4ab67 | 76 | * information such as the number of KeyValuePairs in the store, and whether the |
Jonathan Austin |
1:8aa5cdb4ab67 | 77 | * store has been initialised. |
Jonathan Austin |
1:8aa5cdb4ab67 | 78 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 79 | * After the KeyValueStore struct, KeyValuePairs are arranged contiguously until |
Jonathan Austin |
1:8aa5cdb4ab67 | 80 | * the end of the block used as persistent storage. |
Jonathan Austin |
1:8aa5cdb4ab67 | 81 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 82 | * |-------8-------|--------48-------|-----|---------48--------| |
Jonathan Austin |
1:8aa5cdb4ab67 | 83 | * | KeyValueStore | KeyValuePair[0] | ... | KeyValuePair[N-1] | |
Jonathan Austin |
1:8aa5cdb4ab67 | 84 | * |---------------|-----------------|-----|-------------------| |
Jonathan Austin |
1:8aa5cdb4ab67 | 85 | */ |
Jonathan Austin |
1:8aa5cdb4ab67 | 86 | class MicroBitStorage |
Jonathan Austin |
1:8aa5cdb4ab67 | 87 | { |
Jonathan Austin |
1:8aa5cdb4ab67 | 88 | /** |
Jonathan Austin |
1:8aa5cdb4ab67 | 89 | * Function for copying words from one location to another. |
Jonathan Austin |
1:8aa5cdb4ab67 | 90 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 91 | * @param from the address to copy data from. |
Jonathan Austin |
1:8aa5cdb4ab67 | 92 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 93 | * @param to the address to copy the data to. |
Jonathan Austin |
1:8aa5cdb4ab67 | 94 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 95 | * @param sizeInWords the number of words to copy |
Jonathan Austin |
1:8aa5cdb4ab67 | 96 | */ |
Jonathan Austin |
1:8aa5cdb4ab67 | 97 | void flashCopy(uint32_t* from, uint32_t* to, int sizeInWords); |
Jonathan Austin |
1:8aa5cdb4ab67 | 98 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 99 | /** |
Jonathan Austin |
1:8aa5cdb4ab67 | 100 | * Function for populating the scratch page with a KeyValueStore. |
Jonathan Austin |
1:8aa5cdb4ab67 | 101 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 102 | * @param store the KeyValueStore struct to write to the scratch page. |
Jonathan Austin |
1:8aa5cdb4ab67 | 103 | */ |
Jonathan Austin |
1:8aa5cdb4ab67 | 104 | void scratchKeyValueStore(KeyValueStore store); |
Jonathan Austin |
1:8aa5cdb4ab67 | 105 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 106 | /** |
Jonathan Austin |
1:8aa5cdb4ab67 | 107 | * Function for populating the scratch page with a KeyValuePair. |
Jonathan Austin |
1:8aa5cdb4ab67 | 108 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 109 | * @param pair the KeyValuePair struct to write to the scratch page. |
Jonathan Austin |
1:8aa5cdb4ab67 | 110 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 111 | * @param flashPointer the pointer in flash where this KeyValuePair resides. This pointer |
Jonathan Austin |
1:8aa5cdb4ab67 | 112 | * is used to determine the offset into the scratch page, where the KeyValuePair should |
Jonathan Austin |
1:8aa5cdb4ab67 | 113 | * be written. |
Jonathan Austin |
1:8aa5cdb4ab67 | 114 | */ |
Jonathan Austin |
1:8aa5cdb4ab67 | 115 | void scratchKeyValuePair(KeyValuePair pair, uint32_t* flashPointer); |
Jonathan Austin |
1:8aa5cdb4ab67 | 116 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 117 | public: |
Jonathan Austin |
1:8aa5cdb4ab67 | 118 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 119 | /** |
Jonathan Austin |
1:8aa5cdb4ab67 | 120 | * Default constructor. |
Jonathan Austin |
1:8aa5cdb4ab67 | 121 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 122 | * Creates an instance of MicroBitStorage which acts like a KeyValueStore |
Jonathan Austin |
1:8aa5cdb4ab67 | 123 | * that allows the retrieval, addition and deletion of KeyValuePairs. |
Jonathan Austin |
1:8aa5cdb4ab67 | 124 | */ |
Jonathan Austin |
1:8aa5cdb4ab67 | 125 | MicroBitStorage(); |
Jonathan Austin |
1:8aa5cdb4ab67 | 126 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 127 | /** |
Jonathan Austin |
1:8aa5cdb4ab67 | 128 | * Writes the given number of bytes to the address specified. |
Jonathan Austin |
1:8aa5cdb4ab67 | 129 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 130 | * @param buffer the data to write. |
Jonathan Austin |
1:8aa5cdb4ab67 | 131 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 132 | * @param address the location in memory to write to. |
Jonathan Austin |
1:8aa5cdb4ab67 | 133 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 134 | * @param length the number of bytes to write. |
Jonathan Austin |
1:8aa5cdb4ab67 | 135 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 136 | * @note currently not implemented. |
Jonathan Austin |
1:8aa5cdb4ab67 | 137 | */ |
Jonathan Austin |
1:8aa5cdb4ab67 | 138 | int writeBytes(uint8_t *buffer, uint32_t address, int length); |
Jonathan Austin |
1:8aa5cdb4ab67 | 139 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 140 | /** |
Jonathan Austin |
1:8aa5cdb4ab67 | 141 | * Method for erasing a page in flash. |
Jonathan Austin |
1:8aa5cdb4ab67 | 142 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 143 | * @param page_address Address of the first word in the page to be erased. |
Jonathan Austin |
1:8aa5cdb4ab67 | 144 | */ |
Jonathan Austin |
1:8aa5cdb4ab67 | 145 | void flashPageErase(uint32_t * page_address); |
Jonathan Austin |
1:8aa5cdb4ab67 | 146 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 147 | /** |
Jonathan Austin |
1:8aa5cdb4ab67 | 148 | * Method for writing a word of data in flash with a value. |
Jonathan Austin |
1:8aa5cdb4ab67 | 149 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 150 | * @param address Address of the word to change. |
Jonathan Austin |
1:8aa5cdb4ab67 | 151 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 152 | * @param value Value to be written to flash. |
Jonathan Austin |
1:8aa5cdb4ab67 | 153 | */ |
Jonathan Austin |
1:8aa5cdb4ab67 | 154 | void flashWordWrite(uint32_t * address, uint32_t value); |
Jonathan Austin |
1:8aa5cdb4ab67 | 155 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 156 | /** |
Jonathan Austin |
1:8aa5cdb4ab67 | 157 | * Places a given key, and it's corresponding value into flash at the earliest |
Jonathan Austin |
1:8aa5cdb4ab67 | 158 | * available point. |
Jonathan Austin |
1:8aa5cdb4ab67 | 159 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 160 | * @param key the unique name that should be used as an identifier for the given data. |
Jonathan Austin |
1:8aa5cdb4ab67 | 161 | * The key is presumed to be null terminated. |
Jonathan Austin |
1:8aa5cdb4ab67 | 162 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 163 | * @param data a pointer to the beginning of the data to be persisted. |
Jonathan Austin |
1:8aa5cdb4ab67 | 164 | * |
LancasterUniversity | 35:8ce23bc1af38 | 165 | * @return MICROBIT_OK on success, or MICROBIT_NO_RESOURCES if the storage page is full |
Jonathan Austin |
1:8aa5cdb4ab67 | 166 | */ |
LancasterUniversity | 35:8ce23bc1af38 | 167 | int put(const char* key, uint8_t* data); |
Jonathan Austin |
1:8aa5cdb4ab67 | 168 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 169 | /** |
Jonathan Austin |
1:8aa5cdb4ab67 | 170 | * Places a given key, and it's corresponding value into flash at the earliest |
Jonathan Austin |
1:8aa5cdb4ab67 | 171 | * available point. |
Jonathan Austin |
1:8aa5cdb4ab67 | 172 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 173 | * @param key the unique name that should be used as an identifier for the given data. |
Jonathan Austin |
1:8aa5cdb4ab67 | 174 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 175 | * @param data a pointer to the beginning of the data to be persisted. |
Jonathan Austin |
1:8aa5cdb4ab67 | 176 | * |
LancasterUniversity | 35:8ce23bc1af38 | 177 | * @return MICROBIT_OK on success, or MICROBIT_NO_RESOURCES if the storage page is full |
Jonathan Austin |
1:8aa5cdb4ab67 | 178 | */ |
LancasterUniversity | 35:8ce23bc1af38 | 179 | int put(ManagedString key, uint8_t* data); |
Jonathan Austin |
1:8aa5cdb4ab67 | 180 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 181 | /** |
Jonathan Austin |
1:8aa5cdb4ab67 | 182 | * Retreives a KeyValuePair identified by a given key. |
Jonathan Austin |
1:8aa5cdb4ab67 | 183 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 184 | * @param key the unique name used to identify a KeyValuePair in flash. |
Jonathan Austin |
1:8aa5cdb4ab67 | 185 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 186 | * @return a pointer to a heap allocated KeyValuePair struct, this pointer will be |
Jonathan Austin |
1:8aa5cdb4ab67 | 187 | * NULL if the key was not found in storage. |
Jonathan Austin |
1:8aa5cdb4ab67 | 188 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 189 | * @note it is up to the user to free memory after use. |
Jonathan Austin |
1:8aa5cdb4ab67 | 190 | */ |
Jonathan Austin |
1:8aa5cdb4ab67 | 191 | KeyValuePair* get(const char* key); |
Jonathan Austin |
1:8aa5cdb4ab67 | 192 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 193 | /** |
Jonathan Austin |
1:8aa5cdb4ab67 | 194 | * Retreives a KeyValuePair identified by a given key. |
Jonathan Austin |
1:8aa5cdb4ab67 | 195 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 196 | * @param key the unique name used to identify a KeyValuePair in flash. |
Jonathan Austin |
1:8aa5cdb4ab67 | 197 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 198 | * @return a pointer to a heap allocated KeyValuePair struct, this pointer will be |
Jonathan Austin |
1:8aa5cdb4ab67 | 199 | * NULL if the key was not found in storage. |
Jonathan Austin |
1:8aa5cdb4ab67 | 200 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 201 | * @note it is up to the user to free memory after use. |
Jonathan Austin |
1:8aa5cdb4ab67 | 202 | */ |
Jonathan Austin |
1:8aa5cdb4ab67 | 203 | KeyValuePair* get(ManagedString key); |
Jonathan Austin |
1:8aa5cdb4ab67 | 204 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 205 | /** |
Jonathan Austin |
1:8aa5cdb4ab67 | 206 | * Removes a KeyValuePair identified by a given key. |
Jonathan Austin |
1:8aa5cdb4ab67 | 207 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 208 | * @param key the unique name used to identify a KeyValuePair in flash. |
Jonathan Austin |
1:8aa5cdb4ab67 | 209 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 210 | * @return MICROBIT_OK on success, or MICROBIT_NO_DATA if the given key |
Jonathan Austin |
1:8aa5cdb4ab67 | 211 | * was not found in flash. |
Jonathan Austin |
1:8aa5cdb4ab67 | 212 | */ |
Jonathan Austin |
1:8aa5cdb4ab67 | 213 | int remove(const char* key); |
Jonathan Austin |
1:8aa5cdb4ab67 | 214 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 215 | /** |
Jonathan Austin |
1:8aa5cdb4ab67 | 216 | * Removes a KeyValuePair identified by a given key. |
Jonathan Austin |
1:8aa5cdb4ab67 | 217 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 218 | * @param key the unique name used to identify a KeyValuePair in flash. |
Jonathan Austin |
1:8aa5cdb4ab67 | 219 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 220 | * @return MICROBIT_OK on success, or MICROBIT_NO_DATA if the given key |
Jonathan Austin |
1:8aa5cdb4ab67 | 221 | * was not found in flash. |
Jonathan Austin |
1:8aa5cdb4ab67 | 222 | */ |
Jonathan Austin |
1:8aa5cdb4ab67 | 223 | int remove(ManagedString key); |
Jonathan Austin |
1:8aa5cdb4ab67 | 224 | |
Jonathan Austin |
1:8aa5cdb4ab67 | 225 | /** |
Jonathan Austin |
1:8aa5cdb4ab67 | 226 | * The size of the flash based KeyValueStore. |
Jonathan Austin |
1:8aa5cdb4ab67 | 227 | * |
Jonathan Austin |
1:8aa5cdb4ab67 | 228 | * @return the number of entries in the key value store |
Jonathan Austin |
1:8aa5cdb4ab67 | 229 | */ |
Jonathan Austin |
1:8aa5cdb4ab67 | 230 | int size(); |
Jonathan Austin |
1:8aa5cdb4ab67 | 231 | }; |
Jonathan Austin |
1:8aa5cdb4ab67 | 232 | |
LancasterUniversity | 3:d86a4ddc1867 | 233 | #endif |