From 8a72c4f75cd0e5abaf0cfca253a558aafaf1a757 Mon Sep 17 00:00:00 2001 From: Rijn Buve Date: Wed, 5 Dec 2018 10:41:31 +0100 Subject: [PATCH 01/31] Fixed copyright in LICENSE --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index d645695..2ed96dd 100644 --- a/LICENSE +++ b/LICENSE @@ -187,7 +187,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright 2018, Stichting Mapcode Foundation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From a964e937b60b3353967df0e465f84a0e4b639fd7 Mon Sep 17 00:00:00 2001 From: Rijn Buve Date: Wed, 5 Dec 2018 12:50:20 +0100 Subject: [PATCH 02/31] Fixed LICENSE file --- LICENSE | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/LICENSE b/LICENSE index 2ed96dd..59cfcac 100644 --- a/LICENSE +++ b/LICENSE @@ -176,17 +176,6 @@ END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - Copyright 2018, Stichting Mapcode Foundation Licensed under the Apache License, Version 2.0 (the "License"); From 004fda120bd77b3931b374b2d8fb63f722d0a444 Mon Sep 17 00:00:00 2001 From: Rijn Buve Date: Fri, 13 Sep 2019 15:07:43 +0200 Subject: [PATCH 03/31] Fixed mapcode_legacy.h errors --- mapcodelib/mapcode_legacy.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mapcodelib/mapcode_legacy.h b/mapcodelib/mapcode_legacy.h index 3330ccd..82948d4 100644 --- a/mapcodelib/mapcode_legacy.h +++ b/mapcodelib/mapcode_legacy.h @@ -29,8 +29,8 @@ extern "C" { /** * List of #defines to support legacy systems. */ -#define decodeMapcodeToLatLon(latDeg, lonDeg, mapcode, territory) decodeUtf8MapcodeToLatLon(latDeg, lonDeg, NULL, mapcode, territory) -#define compareWithMapcodeFormat(utf8, canContainTerritory) compareUtf8WithMapcodeFormat(utf8) +#define decodeMapcodeToLatLon(latDeg, lonDeg, mapcode, territory) decodeMapcodeToLatLonUtf(latDeg, lonDeg, mapcode, territory, NULL) +#define compareWithMapcodeFormat(utf8, canContainTerritory) compareWithMapcodeFormatUtf8(utf8) #define convertTerritoryIsoNameToCode getTerritoryCode #define coord2mc(results, lat, lon, territory) encodeLatLonToMapcodes_Deprecated(results, lat, lon,territory, 0) From 27ac86178958a5346daab900289b8a3058875869 Mon Sep 17 00:00:00 2001 From: Rijn Buve Date: Fri, 20 Sep 2019 12:15:26 +0200 Subject: [PATCH 04/31] Fixed legacy call --- mapcodelib/mapcode_legacy.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapcodelib/mapcode_legacy.h b/mapcodelib/mapcode_legacy.h index 82948d4..1caf871 100644 --- a/mapcodelib/mapcode_legacy.h +++ b/mapcodelib/mapcode_legacy.h @@ -29,7 +29,7 @@ extern "C" { /** * List of #defines to support legacy systems. */ -#define decodeMapcodeToLatLon(latDeg, lonDeg, mapcode, territory) decodeMapcodeToLatLonUtf(latDeg, lonDeg, mapcode, territory, NULL) +#define decodeMapcodeToLatLon(latDeg, lonDeg, mapcode, territory) decodeMapcodeToLatLonUtf8(latDeg, lonDeg, mapcode, territory, NULL) #define compareWithMapcodeFormat(utf8, canContainTerritory) compareWithMapcodeFormatUtf8(utf8) #define convertTerritoryIsoNameToCode getTerritoryCode From d3d3e58b5f01a86cbcc73beae0436cb69dbdb4c2 Mon Sep 17 00:00:00 2001 From: Rijn Buve Date: Mon, 20 Dec 2021 17:31:26 +0100 Subject: [PATCH 05/31] Corrected README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f78b81e..088069d 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ decode Mapcodes. This produces the following help text: - MAPCODE (version 2.5.2) + MAPCODE (version 2.5.5) Copyright (C) 2014-2017 Stichting Mapcode Foundation Usage: From f1c0ce4170eaa76bfce3fb5b26600b5560cb387a Mon Sep 17 00:00:00 2001 From: Rijn Buve Date: Mon, 6 Jan 2025 09:33:30 +0100 Subject: [PATCH 06/31] Removed specific warnings that occur on latest Mac compilers --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7742073..f614bc5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,7 @@ project(mapcode_cpp) # NO_FAST_ENCODE - Drop fast encoding support - only for internal use. set(MAPCODE_OPTIONS "") -set(MAPCODE_WARNING_OPTIONS "-Wall -Werror -Wextra") +set(MAPCODE_WARNING_OPTIONS "-Wall -Werror -Wextra -Wno-pointer-to-int-cast -Wno-deprecated-declarations -Wno-unused-but-set-variable") set(MAPCODE_SANITIZER_COMPILER_OPTIONS "-fsanitize=address -fno-common -fno-optimize-sibling-calls -fno-omit-frame-pointer") set(MAPCODE_SANITIZER_LINKER_OPTIONS "-fsanitize=address") @@ -110,4 +110,4 @@ target_link_libraries(mapcode LINK_PUBLIC mapcodelib) target_link_libraries(mapcode LINK_PUBLIC Threads::Threads) target_link_libraries(mapcode LINK_PUBLIC ${M_LIB}) -install(TARGETS mapcode DESTINATION /usr/local/bin) \ No newline at end of file +install(TARGETS mapcode DESTINATION /usr/local/bin) From 4117015a230f102929bb064b53e037b31adf1d9c Mon Sep 17 00:00:00 2001 From: Rijn Buve Date: Thu, 21 Aug 2025 15:34:31 +0200 Subject: [PATCH 07/31] Fixed CMake version --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f614bc5..32492c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -cmake_minimum_required(VERSION 3.3) +cmake_minimum_required(VERSION 3.5) project(mapcode_cpp) # The debug configuration adds the "address sanitizer" to check for out of bounds behavior and such. From 062dfadcde2a87ac255aae3f78f867b67dad15c0 Mon Sep 17 00:00:00 2001 From: Rijn Buve Date: Wed, 10 Sep 2025 10:00:05 +0200 Subject: [PATCH 08/31] Updated to 2025 copyrights and small clean up --- mapcodelib/internal_alphabet_recognizer.h | 28 +- mapcodelib/internal_data.h | 29 +- mapcodelib/internal_iso3166_data.h | 28 +- mapcodelib/internal_territory_alphabets.h | 29 +- mapcodelib/internal_territory_names_af.h | 28 +- mapcodelib/internal_territory_names_ar.h | 28 +- mapcodelib/internal_territory_names_be.h | 28 +- mapcodelib/internal_territory_names_cn.h | 28 +- mapcodelib/internal_territory_names_cs.h | 28 +- mapcodelib/internal_territory_names_da.h | 28 +- mapcodelib/internal_territory_names_de.h | 28 +- mapcodelib/internal_territory_names_en.h | 28 +- mapcodelib/internal_territory_names_es.h | 28 +- mapcodelib/internal_territory_names_fi.h | 28 +- mapcodelib/internal_territory_names_fr.h | 28 +- mapcodelib/internal_territory_names_he.h | 28 +- mapcodelib/internal_territory_names_hi.h | 28 +- mapcodelib/internal_territory_names_hr.h | 28 +- mapcodelib/internal_territory_names_id.h | 28 +- mapcodelib/internal_territory_names_it.h | 28 +- mapcodelib/internal_territory_names_ja.h | 28 +- mapcodelib/internal_territory_names_ko.h | 28 +- mapcodelib/internal_territory_names_local.h | 28 +- mapcodelib/internal_territory_names_nl.h | 28 +- mapcodelib/internal_territory_names_no.h | 28 +- mapcodelib/internal_territory_names_pl.h | 28 +- mapcodelib/internal_territory_names_pt.h | 28 +- mapcodelib/internal_territory_names_ru.h | 28 +- mapcodelib/internal_territory_names_sv.h | 28 +- mapcodelib/internal_territory_names_sw.h | 28 +- mapcodelib/internal_territory_names_tr.h | 28 +- mapcodelib/internal_territory_names_uk.h | 28 +- mapcodelib/internal_territory_search.h | 28 +- mapcodelib/mapcode_alphabets.h | 30 +- mapcodelib/mapcode_legacy.c | 71 +- mapcodelib/mapcode_legacy.h | 56 +- mapcodelib/mapcode_territories.h | 30 +- mapcodelib/mapcoder.c | 1649 +++++++++++-------- mapcodelib/mapcoder.h | 200 +-- test/clean.sh | 16 + test/decode_test.h | 33 +- test/run_all.sh | 16 + test/run_compare.sh | 16 + test/run_gcov.sh | 17 +- test/run_gprof.sh | 16 + test/run_normal.sh | 16 + test/run_sanitizer.sh | 16 + test/run_valgrind.sh | 16 + test/test_territories.h | 28 +- test/unittest.c | 32 +- utility/compile.sh | 17 +- utility/mapcode.cpp | 338 ++-- 52 files changed, 1957 insertions(+), 1582 deletions(-) diff --git a/mapcodelib/internal_alphabet_recognizer.h b/mapcodelib/internal_alphabet_recognizer.h index a4e5e30..b18be33 100644 --- a/mapcodelib/internal_alphabet_recognizer.h +++ b/mapcodelib/internal_alphabet_recognizer.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_ALPHABET_RECOGNIZER_H__ #define __INTERNAL_ALPHABET_RECOGNIZER_H__ diff --git a/mapcodelib/internal_data.h b/mapcodelib/internal_data.h index b75f087..eaf9a06 100644 --- a/mapcodelib/internal_data.h +++ b/mapcodelib/internal_data.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_DATA_H__ #define __INTERNAL_DATA_H__ @@ -16549,4 +16547,3 @@ static const TerritoryBoundary TERRITORY_BOUNDARIES[MAPCODE_BOUNDARY_MAX + 1] = #endif #endif // __INTERNAL_DATA_H__ - diff --git a/mapcodelib/internal_iso3166_data.h b/mapcodelib/internal_iso3166_data.h index 1a2ace2..f48963a 100644 --- a/mapcodelib/internal_iso3166_data.h +++ b/mapcodelib/internal_iso3166_data.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_ISO3166_DATA_H__ #define __INTERNAL_ISO3166_DATA_H__ diff --git a/mapcodelib/internal_territory_alphabets.h b/mapcodelib/internal_territory_alphabets.h index 1981e84..de9c729 100644 --- a/mapcodelib/internal_territory_alphabets.h +++ b/mapcodelib/internal_territory_alphabets.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_TERRITORY_ALPHABETS_H__ #define __INTERNAL_TERRITORY_ALPHABETS_H__ @@ -565,4 +563,3 @@ static const TerritoryAlphabets ALPHABETS_FOR_TERRITORY[_TERRITORY_MAX - _TERRIT #endif #endif // __INTERNAL_TERRITORY_ALPHABETS_H__ - diff --git a/mapcodelib/internal_territory_names_af.h b/mapcodelib/internal_territory_names_af.h index e99ec55..303aa2e 100644 --- a/mapcodelib/internal_territory_names_af.h +++ b/mapcodelib/internal_territory_names_af.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_TERRITORY_NAMES_AF_H__ #define __INTERNAL_TERRITORY_NAMES_AF_H__ diff --git a/mapcodelib/internal_territory_names_ar.h b/mapcodelib/internal_territory_names_ar.h index 8437e8a..423847a 100644 --- a/mapcodelib/internal_territory_names_ar.h +++ b/mapcodelib/internal_territory_names_ar.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_TERRITORY_NAMES_AR_H__ #define __INTERNAL_TERRITORY_NAMES_AR_H__ diff --git a/mapcodelib/internal_territory_names_be.h b/mapcodelib/internal_territory_names_be.h index c057cfe..d491447 100644 --- a/mapcodelib/internal_territory_names_be.h +++ b/mapcodelib/internal_territory_names_be.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_TERRITORY_NAMES_BE_H__ #define __INTERNAL_TERRITORY_NAMES_BE_H__ diff --git a/mapcodelib/internal_territory_names_cn.h b/mapcodelib/internal_territory_names_cn.h index ff40848..bcf64d0 100644 --- a/mapcodelib/internal_territory_names_cn.h +++ b/mapcodelib/internal_territory_names_cn.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_TERRITORY_NAMES_CN_H__ #define __INTERNAL_TERRITORY_NAMES_CN_H__ diff --git a/mapcodelib/internal_territory_names_cs.h b/mapcodelib/internal_territory_names_cs.h index 5f0820f..67ad194 100644 --- a/mapcodelib/internal_territory_names_cs.h +++ b/mapcodelib/internal_territory_names_cs.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_TERRITORY_NAMES_CS_H__ #define __INTERNAL_TERRITORY_NAMES_CS_H__ diff --git a/mapcodelib/internal_territory_names_da.h b/mapcodelib/internal_territory_names_da.h index b350447..071251e 100644 --- a/mapcodelib/internal_territory_names_da.h +++ b/mapcodelib/internal_territory_names_da.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_TERRITORY_NAMES_DA_H__ #define __INTERNAL_TERRITORY_NAMES_DA_H__ diff --git a/mapcodelib/internal_territory_names_de.h b/mapcodelib/internal_territory_names_de.h index 55024d6..d26e017 100644 --- a/mapcodelib/internal_territory_names_de.h +++ b/mapcodelib/internal_territory_names_de.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_TERRITORY_NAMES_DE_H__ #define __INTERNAL_TERRITORY_NAMES_DE_H__ diff --git a/mapcodelib/internal_territory_names_en.h b/mapcodelib/internal_territory_names_en.h index 333524a..1a7432a 100644 --- a/mapcodelib/internal_territory_names_en.h +++ b/mapcodelib/internal_territory_names_en.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_TERRITORY_NAMES_EN_H__ #define __INTERNAL_TERRITORY_NAMES_EN_H__ diff --git a/mapcodelib/internal_territory_names_es.h b/mapcodelib/internal_territory_names_es.h index 2403744..76f08fe 100644 --- a/mapcodelib/internal_territory_names_es.h +++ b/mapcodelib/internal_territory_names_es.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_TERRITORY_NAMES_ES_H__ #define __INTERNAL_TERRITORY_NAMES_ES_H__ diff --git a/mapcodelib/internal_territory_names_fi.h b/mapcodelib/internal_territory_names_fi.h index ae24f71..0fece2e 100644 --- a/mapcodelib/internal_territory_names_fi.h +++ b/mapcodelib/internal_territory_names_fi.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_TERRITORY_NAMES_FI_H__ #define __INTERNAL_TERRITORY_NAMES_FI_H__ diff --git a/mapcodelib/internal_territory_names_fr.h b/mapcodelib/internal_territory_names_fr.h index dc8f444..e8afcff 100644 --- a/mapcodelib/internal_territory_names_fr.h +++ b/mapcodelib/internal_territory_names_fr.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_TERRITORY_NAMES_FR_H__ #define __INTERNAL_TERRITORY_NAMES_FR_H__ diff --git a/mapcodelib/internal_territory_names_he.h b/mapcodelib/internal_territory_names_he.h index d271209..b463d5a 100644 --- a/mapcodelib/internal_territory_names_he.h +++ b/mapcodelib/internal_territory_names_he.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_TERRITORY_NAMES_HE_H__ #define __INTERNAL_TERRITORY_NAMES_HE_H__ diff --git a/mapcodelib/internal_territory_names_hi.h b/mapcodelib/internal_territory_names_hi.h index 0e6bbbe..c301647 100644 --- a/mapcodelib/internal_territory_names_hi.h +++ b/mapcodelib/internal_territory_names_hi.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_TERRITORY_NAMES_HI_H__ #define __INTERNAL_TERRITORY_NAMES_HI_H__ diff --git a/mapcodelib/internal_territory_names_hr.h b/mapcodelib/internal_territory_names_hr.h index 494a567..9fc918c 100644 --- a/mapcodelib/internal_territory_names_hr.h +++ b/mapcodelib/internal_territory_names_hr.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_TERRITORY_NAMES_HR_H__ #define __INTERNAL_TERRITORY_NAMES_HR_H__ diff --git a/mapcodelib/internal_territory_names_id.h b/mapcodelib/internal_territory_names_id.h index 3e6a306..c5992f0 100644 --- a/mapcodelib/internal_territory_names_id.h +++ b/mapcodelib/internal_territory_names_id.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_TERRITORY_NAMES_ID_H__ #define __INTERNAL_TERRITORY_NAMES_ID_H__ diff --git a/mapcodelib/internal_territory_names_it.h b/mapcodelib/internal_territory_names_it.h index 4ea3e14..efe6524 100644 --- a/mapcodelib/internal_territory_names_it.h +++ b/mapcodelib/internal_territory_names_it.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_TERRITORY_NAMES_IT_H__ #define __INTERNAL_TERRITORY_NAMES_IT_H__ diff --git a/mapcodelib/internal_territory_names_ja.h b/mapcodelib/internal_territory_names_ja.h index b9e038c..906bd1d 100644 --- a/mapcodelib/internal_territory_names_ja.h +++ b/mapcodelib/internal_territory_names_ja.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_TERRITORY_NAMES_JA_H__ #define __INTERNAL_TERRITORY_NAMES_JA_H__ diff --git a/mapcodelib/internal_territory_names_ko.h b/mapcodelib/internal_territory_names_ko.h index ac1fe23..4fbe25a 100644 --- a/mapcodelib/internal_territory_names_ko.h +++ b/mapcodelib/internal_territory_names_ko.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_TERRITORY_NAMES_KO_H__ #define __INTERNAL_TERRITORY_NAMES_KO_H__ diff --git a/mapcodelib/internal_territory_names_local.h b/mapcodelib/internal_territory_names_local.h index 1d62c6e..5dc0fa9 100644 --- a/mapcodelib/internal_territory_names_local.h +++ b/mapcodelib/internal_territory_names_local.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_TERRITORY_NAMES_LOCAL_H__ #define __INTERNAL_TERRITORY_NAMES_LOCAL_H__ diff --git a/mapcodelib/internal_territory_names_nl.h b/mapcodelib/internal_territory_names_nl.h index 8e037bf..1527b92 100644 --- a/mapcodelib/internal_territory_names_nl.h +++ b/mapcodelib/internal_territory_names_nl.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_TERRITORY_NAMES_NL_H__ #define __INTERNAL_TERRITORY_NAMES_NL_H__ diff --git a/mapcodelib/internal_territory_names_no.h b/mapcodelib/internal_territory_names_no.h index f5bbed3..b178446 100644 --- a/mapcodelib/internal_territory_names_no.h +++ b/mapcodelib/internal_territory_names_no.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_TERRITORY_NAMES_NO_H__ #define __INTERNAL_TERRITORY_NAMES_NO_H__ diff --git a/mapcodelib/internal_territory_names_pl.h b/mapcodelib/internal_territory_names_pl.h index c860a92..b2aa80e 100644 --- a/mapcodelib/internal_territory_names_pl.h +++ b/mapcodelib/internal_territory_names_pl.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_TERRITORY_NAMES_PL_H__ #define __INTERNAL_TERRITORY_NAMES_PL_H__ diff --git a/mapcodelib/internal_territory_names_pt.h b/mapcodelib/internal_territory_names_pt.h index 297bbb4..02985ca 100644 --- a/mapcodelib/internal_territory_names_pt.h +++ b/mapcodelib/internal_territory_names_pt.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_TERRITORY_NAMES_PT_H__ #define __INTERNAL_TERRITORY_NAMES_PT_H__ diff --git a/mapcodelib/internal_territory_names_ru.h b/mapcodelib/internal_territory_names_ru.h index e491e3c..be8ec75 100644 --- a/mapcodelib/internal_territory_names_ru.h +++ b/mapcodelib/internal_territory_names_ru.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_TERRITORY_NAMES_RU_H__ #define __INTERNAL_TERRITORY_NAMES_RU_H__ diff --git a/mapcodelib/internal_territory_names_sv.h b/mapcodelib/internal_territory_names_sv.h index be6eb96..4c7f88f 100644 --- a/mapcodelib/internal_territory_names_sv.h +++ b/mapcodelib/internal_territory_names_sv.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_TERRITORY_NAMES_SV_H__ #define __INTERNAL_TERRITORY_NAMES_SV_H__ diff --git a/mapcodelib/internal_territory_names_sw.h b/mapcodelib/internal_territory_names_sw.h index 91e300b..9f5c3c8 100644 --- a/mapcodelib/internal_territory_names_sw.h +++ b/mapcodelib/internal_territory_names_sw.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_TERRITORY_NAMES_SW_H__ #define __INTERNAL_TERRITORY_NAMES_SW_H__ diff --git a/mapcodelib/internal_territory_names_tr.h b/mapcodelib/internal_territory_names_tr.h index cd4efd4..f1926cc 100644 --- a/mapcodelib/internal_territory_names_tr.h +++ b/mapcodelib/internal_territory_names_tr.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_TERRITORY_NAMES_TR_H__ #define __INTERNAL_TERRITORY_NAMES_TR_H__ diff --git a/mapcodelib/internal_territory_names_uk.h b/mapcodelib/internal_territory_names_uk.h index 25e4d65..6007cc7 100644 --- a/mapcodelib/internal_territory_names_uk.h +++ b/mapcodelib/internal_territory_names_uk.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_TERRITORY_NAMES_UK_H__ #define __INTERNAL_TERRITORY_NAMES_UK_H__ diff --git a/mapcodelib/internal_territory_search.h b/mapcodelib/internal_territory_search.h index f928083..7e573e9 100644 --- a/mapcodelib/internal_territory_search.h +++ b/mapcodelib/internal_territory_search.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __INTERNAL_TERRITORY_SEARCH_H__ #define __INTERNAL_TERRITORY_SEARCH_H__ diff --git a/mapcodelib/mapcode_alphabets.h b/mapcodelib/mapcode_alphabets.h index 830e6a5..90bd4c8 100644 --- a/mapcodelib/mapcode_alphabets.h +++ b/mapcodelib/mapcode_alphabets.h @@ -1,23 +1,22 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __MAPCODE_ALPHABETS_H__ #define __MAPCODE_ALPHABETS_H__ #ifdef __cplusplus extern "C" { + #endif /** @@ -65,4 +64,3 @@ enum Alphabet { #endif #endif // __MAPCODE_ALPHABETS_H__ - diff --git a/mapcodelib/mapcode_legacy.c b/mapcodelib/mapcode_legacy.c index a397696..b897e8a 100644 --- a/mapcodelib/mapcode_legacy.c +++ b/mapcodelib/mapcode_legacy.c @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #include #include @@ -25,26 +23,27 @@ */ static Mapcodes GLOBAL_RESULT; static char GLOBAL_MAKEISO_BUFFER[2 * (MAX_ISOCODE_LEN + 1)]; -static char *GLOBAL_MAKEISO_PTR; +static char* GLOBAL_MAKEISO_PTR; int encodeLatLonToMapcodes_Deprecated( - char **mapcodesAndTerritories, - double latDeg, - double lonDeg, - enum Territory territory, - int extraDigits) { - char **v = mapcodesAndTerritories; + char** mapcodesAndTerritories, + double latDeg, + double lonDeg, + enum Territory territory, + int extraDigits) { + char** v = mapcodesAndTerritories; encodeLatLonToMapcodes(&GLOBAL_RESULT, latDeg, lonDeg, territory, extraDigits); if (v) { int i; for (i = 0; i < GLOBAL_RESULT.count; i++) { - char *s = &GLOBAL_RESULT.mapcode[i][0]; - char *p = strchr(s, ' '); + char* s = &GLOBAL_RESULT.mapcode[i][0]; + char* p = strchr(s, ' '); if (p == NULL) { - v[i * 2 + 1] = (char *) "AAA"; + v[i * 2 + 1] = (char*)"AAA"; v[i * 2] = s; - } else { + } + else { *p++ = 0; v[i * 2 + 1] = s; v[i * 2] = p; @@ -55,15 +54,16 @@ int encodeLatLonToMapcodes_Deprecated( } -const char *convertTerritoryCodeToIsoName_Deprecated( - enum Territory territoryContext, - int useShortName) { +const char* convertTerritoryCodeToIsoName_Deprecated( + enum Territory territoryContext, + int useShortName) { if (GLOBAL_MAKEISO_PTR == GLOBAL_MAKEISO_BUFFER) { GLOBAL_MAKEISO_PTR = GLOBAL_MAKEISO_BUFFER + (MAX_ISOCODE_LEN + 1); - } else { + } + else { GLOBAL_MAKEISO_PTR = GLOBAL_MAKEISO_BUFFER; } - return (const char *) getTerritoryIsoName(GLOBAL_MAKEISO_PTR, territoryContext, useShortName); + return (const char*)getTerritoryIsoName(GLOBAL_MAKEISO_PTR, territoryContext, useShortName); } @@ -74,19 +74,18 @@ static char GLOBAL_ASCII_BUFFER[MAX_MAPCODE_RESULT_LEN]; static UWORD GLOBAL_UTF16_BUFFER[MAX_MAPCODE_RESULT_LEN]; -const char *decodeToRoman_Deprecated(const UWORD *utf16String) { +const char* decodeToRoman_Deprecated(const UWORD* utf16String) { return convertToRoman(GLOBAL_ASCII_BUFFER, MAX_MAPCODE_RESULT_LEN, utf16String); } -const UWORD *encodeToAlphabet_Deprecated(const char *asciiString, +const UWORD* encodeToAlphabet_Deprecated(const char* asciiString, enum Alphabet alphabet) { return convertToAlphabet(GLOBAL_UTF16_BUFFER, MAX_MAPCODE_RESULT_LEN, asciiString, alphabet); } -char *convertToRoman(char *asciiBuffer, int maxLength, const UWORD *unicodeBuffer) { - +char* convertToRoman(char* asciiBuffer, int maxLength, const UWORD* unicodeBuffer) { MapcodeElements mapcodeElements; double lat, lon; enum MapcodeError err; @@ -104,7 +103,7 @@ char *convertToRoman(char *asciiBuffer, int maxLength, const UWORD *unicodeBuffe mapcodeElements.properMapcode, *mapcodeElements.precisionExtension ? "-" : "", mapcodeElements.precisionExtension); - if ((int) strlen(romanized) < maxLength) { + if ((int)strlen(romanized) < maxLength) { strcpy(asciiBuffer, romanized); } } diff --git a/mapcodelib/mapcode_legacy.h b/mapcodelib/mapcode_legacy.h index 1caf871..7b125a5 100644 --- a/mapcodelib/mapcode_legacy.h +++ b/mapcodelib/mapcode_legacy.h @@ -1,24 +1,23 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __MAPCODE_LEGACY_H__ #define __MAPCODE_LEGACY_H__ #ifdef __cplusplus extern "C" { + #endif #include "mapcoder.h" @@ -99,12 +98,12 @@ extern "C" { * The results are stored as pairs (Mapcode, territory name) in: * (results[0], results[1])...(results[(2 * N) - 2], results[(2 * N) - 1]) */ -int encodeLatLonToMapcodes_Deprecated( // Warning: this method is deprecated and not thread-safe. - char **mapcodesAndTerritories, - double latDeg, - double lonDeg, - enum Territory territory, - int extraDigits); +int encodeLatLonToMapcodes_Deprecated( // Warning: this method is deprecated and not thread-safe. + char** mapcodesAndTerritories, + double latDeg, + double lonDeg, + enum Territory territory, + int extraDigits); /** @@ -120,9 +119,9 @@ int encodeLatLonToMapcodes_Deprecated( // Warning: this method is deprecated * Returns: * Pointer to result. String will be empty if territory illegal. */ -const char *convertTerritoryCodeToIsoName_Deprecated( - enum Territory territory, - int useShortName); +const char* convertTerritoryCodeToIsoName_Deprecated( + enum Territory territory, + int useShortName); /** @@ -136,8 +135,7 @@ const char *convertTerritoryCodeToIsoName_Deprecated( * Returns: * Pointer to same buffer as asciiString (allocated by caller), which holds the result. */ -char *convertToRoman(char *asciiString, int maxLength, const UWORD *utf16String); - +char* convertToRoman(char* asciiString, int maxLength, const UWORD* utf16String); /** @@ -152,7 +150,7 @@ char *convertToRoman(char *asciiString, int maxLength, const UWORD *utf16String) * Returns: * Encoded Unicode string, points at buffer from 'utf16String', allocated/deallocated by caller. */ -UWORD *convertToAlphabet(UWORD *utf16String, int maxLength, const char *asciiString, enum Alphabet alphabet); +UWORD* convertToAlphabet(UWORD* utf16String, int maxLength, const char* asciiString, enum Alphabet alphabet); /** @@ -164,7 +162,7 @@ UWORD *convertToAlphabet(UWORD *utf16String, int maxLength, const char *asciiStr * String must NOT be de-allocated by the caller. * It will be overwritten by a subsequent call to this method! */ -const char *decodeToRoman_Deprecated(const UWORD *utf16String); +const char* decodeToRoman_Deprecated(const UWORD* utf16String); /** @@ -175,7 +173,7 @@ const char *decodeToRoman_Deprecated(const UWORD *utf16String); * String must NOT be de-allocated by the caller. * It will be overwritten by a subsequent call to this method! */ -const UWORD *encodeToAlphabet_Deprecated(const char *asciiString, enum Alphabet alphabet); +const UWORD* encodeToAlphabet_Deprecated(const char* asciiString, enum Alphabet alphabet); #ifdef __cplusplus diff --git a/mapcodelib/mapcode_territories.h b/mapcodelib/mapcode_territories.h index 52e5981..95e1da0 100644 --- a/mapcodelib/mapcode_territories.h +++ b/mapcodelib/mapcode_territories.h @@ -1,23 +1,22 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __MAPCODE_TERRITORIES_H__ #define __MAPCODE_TERRITORIES_H__ #ifdef __cplusplus extern "C" { + #endif // *** GENERATED FILE (coords.cpp data2.3.0), DO NOT CHANGE OR PRETTIFY *** @@ -570,4 +569,3 @@ enum Territory { #endif #endif // __MAPCODE_TERRITORIES_H__ - diff --git a/mapcodelib/mapcoder.c b/mapcodelib/mapcoder.c index 312b6d8..8a577b8 100644 --- a/mapcodelib/mapcoder.c +++ b/mapcodelib/mapcoder.c @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #include // strlen strcpy strcat memcpy memmove strstr strchr memcmp #include // atof @@ -54,24 +52,24 @@ #include "internal_territory_names_uk.h" // The constants are also exported as variables, to allow other languages to use them. -char *_MAPCODE_C_VERSION = MAPCODE_C_VERSION; -int _MAX_NR_OF_MAPCODE_RESULTS = MAX_NR_OF_MAPCODE_RESULTS; -int _MAX_PRECISION_DIGITS = MAX_PRECISION_DIGITS; -int _MAX_PROPER_MAPCODE_ASCII_LEN = MAX_PROPER_MAPCODE_ASCII_LEN; -int _MAX_ISOCODE_ASCII_LEN = MAX_ISOCODE_ASCII_LEN; -int _MAX_CLEAN_MAPCODE_ASCII_LEN = MAX_CLEAN_MAPCODE_ASCII_LEN; -int _MAX_MAPCODE_RESULT_ASCII_LEN = MAX_MAPCODE_RESULT_ASCII_LEN; -int _MAX_TERRITORY_FULLNAME_UTF8_LEN = MAX_TERRITORY_FULLNAME_UTF8_LEN; -int _MAX_MAPCODE_RESULT_UTF8_LEN = MAX_MAPCODE_RESULT_UTF8_LEN; -int _MAX_MAPCODE_RESULT_UTF16_LEN = MAX_MAPCODE_RESULT_UTF16_LEN; -int _MAX_ALPHABETS_PER_TERRITORY = MAX_ALPHABETS_PER_TERRITORY; +char* _MAPCODE_C_VERSION = MAPCODE_C_VERSION; +int _MAX_NR_OF_MAPCODE_RESULTS = MAX_NR_OF_MAPCODE_RESULTS; +int _MAX_PRECISION_DIGITS = MAX_PRECISION_DIGITS; +int _MAX_PROPER_MAPCODE_ASCII_LEN = MAX_PROPER_MAPCODE_ASCII_LEN; +int _MAX_ISOCODE_ASCII_LEN = MAX_ISOCODE_ASCII_LEN; +int _MAX_CLEAN_MAPCODE_ASCII_LEN = MAX_CLEAN_MAPCODE_ASCII_LEN; +int _MAX_MAPCODE_RESULT_ASCII_LEN = MAX_MAPCODE_RESULT_ASCII_LEN; +int _MAX_TERRITORY_FULLNAME_UTF8_LEN = MAX_TERRITORY_FULLNAME_UTF8_LEN; +int _MAX_MAPCODE_RESULT_UTF8_LEN = MAX_MAPCODE_RESULT_UTF8_LEN; +int _MAX_MAPCODE_RESULT_UTF16_LEN = MAX_MAPCODE_RESULT_UTF16_LEN; +int _MAX_ALPHABETS_PER_TERRITORY = MAX_ALPHABETS_PER_TERRITORY; #ifdef DEBUG #include -void _TestAssert(int iCondition, const char *cstrFile, int iLine) { +void _TestAssert(int iCondition, const char* cstrFile, int iLine) { static int nrAsserts = 0; if (!iCondition) { fprintf(stderr, "** Assertion failed: file \"%s\", line %d\n", cstrFile, iLine); @@ -140,85 +138,86 @@ static const double METERS_PER_DEGREE_LON = EARTH_CIRCUMFERENCE_X / 360.0; static const int DEBUG_STOP_AT = -1; // to externally test-restrict internal encoding, do not use! typedef struct { - const char *locale; - const char **territoryFullNames; + const char* locale; + const char** territoryFullNames; } LocaleRegistryItem; static const LocaleRegistryItem LOCALE_REGISTRY[] = { - {"AF", TERRITORY_FULL_NAME_AF}, - {"AR", TERRITORY_FULL_NAME_AR}, - {"BE", TERRITORY_FULL_NAME_BE}, - {"CN", TERRITORY_FULL_NAME_CN}, - {"CS", TERRITORY_FULL_NAME_CS}, - {"DA", TERRITORY_FULL_NAME_DA}, - {"DE", TERRITORY_FULL_NAME_DE}, - {"EN", TERRITORY_FULL_NAME_EN}, - {"ES", TERRITORY_FULL_NAME_ES}, - {"FI", TERRITORY_FULL_NAME_FI}, - {"FR", TERRITORY_FULL_NAME_FR}, - {"HE", TERRITORY_FULL_NAME_HE}, - {"HI", TERRITORY_FULL_NAME_HI}, - {"HR", TERRITORY_FULL_NAME_HR}, - {"ID", TERRITORY_FULL_NAME_ID}, - {"IT", TERRITORY_FULL_NAME_IT}, - {"JA", TERRITORY_FULL_NAME_JA}, - {"KO", TERRITORY_FULL_NAME_KO}, - {"NL", TERRITORY_FULL_NAME_NL}, - {"NO", TERRITORY_FULL_NAME_NO}, - {"PT", TERRITORY_FULL_NAME_PT}, - {"PL", TERRITORY_FULL_NAME_PL}, - {"RU", TERRITORY_FULL_NAME_RU}, - {"SV", TERRITORY_FULL_NAME_SV}, - {"SW", TERRITORY_FULL_NAME_SW}, - {"TR", TERRITORY_FULL_NAME_TR}, - {"UK", TERRITORY_FULL_NAME_UK} + {"AF", TERRITORY_FULL_NAME_AF}, + {"AR", TERRITORY_FULL_NAME_AR}, + {"BE", TERRITORY_FULL_NAME_BE}, + {"CN", TERRITORY_FULL_NAME_CN}, + {"CS", TERRITORY_FULL_NAME_CS}, + {"DA", TERRITORY_FULL_NAME_DA}, + {"DE", TERRITORY_FULL_NAME_DE}, + {"EN", TERRITORY_FULL_NAME_EN}, + {"ES", TERRITORY_FULL_NAME_ES}, + {"FI", TERRITORY_FULL_NAME_FI}, + {"FR", TERRITORY_FULL_NAME_FR}, + {"HE", TERRITORY_FULL_NAME_HE}, + {"HI", TERRITORY_FULL_NAME_HI}, + {"HR", TERRITORY_FULL_NAME_HR}, + {"ID", TERRITORY_FULL_NAME_ID}, + {"IT", TERRITORY_FULL_NAME_IT}, + {"JA", TERRITORY_FULL_NAME_JA}, + {"KO", TERRITORY_FULL_NAME_KO}, + {"NL", TERRITORY_FULL_NAME_NL}, + {"NO", TERRITORY_FULL_NAME_NO}, + {"PT", TERRITORY_FULL_NAME_PT}, + {"PL", TERRITORY_FULL_NAME_PL}, + {"RU", TERRITORY_FULL_NAME_RU}, + {"SV", TERRITORY_FULL_NAME_SV}, + {"SW", TERRITORY_FULL_NAME_SW}, + {"TR", TERRITORY_FULL_NAME_TR}, + {"UK", TERRITORY_FULL_NAME_UK} }; // important information about the 8 parents -static const char *PARENTS_3 = "USA,IND,CAN,AUS,MEX,BRA,RUS,CHN,"; -static const char *PARENTS_2 = "US,IN,CA,AU,MX,BR,RU,CN,"; +static const char* PARENTS_3 = "USA,IND,CAN,AUS,MEX,BRA,RUS,CHN,"; +static const char* PARENTS_2 = "US,IN,CA,AU,MX,BR,RU,CN,"; static const enum Territory PARENT_NR[9] = { - TERRITORY_NONE, - TERRITORY_USA, - TERRITORY_IND, - TERRITORY_CAN, - TERRITORY_AUS, - TERRITORY_MEX, - TERRITORY_BRA, - TERRITORY_RUS, - TERRITORY_CHN + TERRITORY_NONE, + TERRITORY_USA, + TERRITORY_IND, + TERRITORY_CAN, + TERRITORY_AUS, + TERRITORY_MEX, + TERRITORY_BRA, + TERRITORY_RUS, + TERRITORY_CHN }; // base-31 alphabet, digits (0-9), consonants (10-30), vowels (31-33) static const char ENCODE_CHARS[34] = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', - 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z', - 'A', 'E', 'U'}; + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', + 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z', + 'A', 'E', 'U' +}; static signed char decodeChar(const char ch) { // base-31 value of ascii character (negative for illegal characters) // special cases -2, -3, -4 for vowels; o and i interpreted as 0 and 1. static const signed char decode_chars[256] = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 32 - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 48 - -1, -2, 10, 11, 12, -3, 13, 14, 15, 1, 16, 17, 18, 19, 20, 0, // 64 - 21, 22, 23, 24, 25, -4, 26, 27, 28, 29, 30, -1, -1, -1, -1, -1, // 80 - -1, -2, 10, 11, 12, -3, 13, 14, 15, 1, 16, 17, 18, 19, 20, 0, // 96 - 21, 22, 23, 24, 25, -4, 26, 27, 28, 29, 30, -1, -1, -1, -1, -1, // 112 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 144 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 176 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 192 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 208 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224 - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 240 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 32 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 48 + -1, -2, 10, 11, 12, -3, 13, 14, 15, 1, 16, 17, 18, 19, 20, 0, // 64 + 21, 22, 23, 24, 25, -4, 26, 27, 28, 29, 30, -1, -1, -1, -1, -1, // 80 + -1, -2, 10, 11, 12, -3, 13, 14, 15, 1, 16, 17, 18, 19, 20, 0, // 96 + 21, 22, 23, 24, 25, -4, 26, 27, 28, 29, 30, -1, -1, -1, -1, -1, // 112 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 144 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 176 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 192 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 208 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 240 }; - return decode_chars[(unsigned char) ch]; // ch can be negative, must be fit to range 0-255. + return decode_chars[(unsigned char)ch]; // ch can be negative, must be fit to range 0-255. } /////////////////////////////////////////////////////////////////////////////////////////////// @@ -238,7 +237,8 @@ double distanceInMeters(double latDeg1, double lonDeg1, double latDeg2, double l ASSERT((-90.0 <= latDeg2) && (latDeg2 <= 90.0)); if (wrapped) { deltaLonDegrees = 360.0 - (lonDeg1 - lonDeg2); - } else { + } + else { deltaLonDegrees = lonDeg2 - lonDeg1; } if (deltaLonDegrees > 180.0) { @@ -258,15 +258,15 @@ double distanceInMeters(double latDeg1, double lonDeg1, double latDeg2, double l // maximum error in meters for a certain nr of high-precision digits static const double MAX_ERROR_IN_METERS[MAX_PRECISION_DIGITS + 1] = { - 7.49, - 1.39, - 0.251, - 0.0462, - 0.00837, - 0.00154, - 0.000279, - 0.0000514, - 0.0000093 + 7.49, + 1.39, + 0.251, + 0.0462, + 0.00837, + 0.00154, + 0.000279, + 0.0000514, + 0.0000093 }; @@ -290,21 +290,22 @@ typedef struct { int lonMicroDeg; // longitude in microdegrees } Point32; -typedef struct { // Point - double lat; // latitude (units depend on situation) - double lon; // longitude (units depend on situation) +typedef struct { + // Point + double lat; // latitude (units depend on situation) + double lon; // longitude (units depend on situation) } Point; -static Point32 convertFractionsToCoord32(const Point *p) { +static Point32 convertFractionsToCoord32(const Point* p) { Point32 p32; - p32.latMicroDeg = (int) floor(p->lat / 810000); - p32.lonMicroDeg = (int) floor(p->lon / 3240000); + p32.latMicroDeg = (int)floor(p->lat / 810000); + p32.lonMicroDeg = (int)floor(p->lon / 3240000); return p32; } -static Point convertFractionsToDegrees(const Point *p) { +static Point convertFractionsToDegrees(const Point* p) { Point pd; pd.lat = p->lat / (810000 * 1000000.0); pd.lon = p->lon / (3240000 * 1000000.0); @@ -312,12 +313,12 @@ static Point convertFractionsToDegrees(const Point *p) { } -static const unsigned char DOUBLE_NAN[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F}; // NAN (Not a Number) -static const unsigned char DOUBLE_INF[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x7F}; // +Infinity +static const unsigned char DOUBLE_NAN[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F}; // NAN (Not a Number) +static const unsigned char DOUBLE_INF[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x7F}; // +Infinity static const unsigned char DOUBLE_MIN_INF[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF}; // -Infinity static enum MapcodeError -convertCoordsToMicrosAndFractions(Point32 *coord32, int *fracLat, int *fracLon, double latDeg, double lonDeg) { +convertCoordsToMicrosAndFractions(Point32* coord32, int* fracLat, int* fracLon, double latDeg, double lonDeg) { double frac; ASSERT(coord32); if (memcmp(&lonDeg, DOUBLE_NAN, 8) == 0 || memcmp(&lonDeg, DOUBLE_INF, 8) == 0 || @@ -327,28 +328,29 @@ convertCoordsToMicrosAndFractions(Point32 *coord32, int *fracLat, int *fracLon, } if (latDeg < -90) { latDeg = -90; - } else if (latDeg > 90) { + } + else if (latDeg > 90) { latDeg = 90; } latDeg += 90; // lat now [0..180] ASSERT((0.0 <= latDeg) && (latDeg <= 180.0)); - latDeg *= (double) 810000000000; + latDeg *= (double)810000000000; frac = floor(latDeg + 0.1); - coord32->latMicroDeg = (int) (frac / (double) 810000); + coord32->latMicroDeg = (int)(frac / (double)810000); if (fracLat) { - frac -= ((double) coord32->latMicroDeg * (double) 810000); - *fracLat = (int) frac; + frac -= ((double)coord32->latMicroDeg * (double)810000); + *fracLat = (int)frac; } coord32->latMicroDeg -= 90000000; lonDeg -= (360.0 * floor(lonDeg / 360)); // lon now in [0..360> ASSERT((0.0 <= lonDeg) && (lonDeg < 360.0)); - lonDeg *= (double) 3240000000000; + lonDeg *= (double)3240000000000; frac = floor(lonDeg + 0.1); - coord32->lonMicroDeg = (int) (frac / (double) 3240000); + coord32->lonMicroDeg = (int)(frac / (double)3240000); if (fracLon) { - frac -= (double) coord32->lonMicroDeg * (double) 3240000; - *fracLon = (int) frac; + frac -= (double)coord32->lonMicroDeg * (double)3240000; + *fracLon = (int)frac; } if (coord32->lonMicroDeg >= 180000000) { coord32->lonMicroDeg -= 360000000; @@ -370,7 +372,8 @@ static int isInRange(int lonMicroDeg, const int minLonMicroDeg, const int maxLon } if (lonMicroDeg < minLonMicroDeg) { lonMicroDeg += 360000000; - } else { + } + else { lonMicroDeg -= 360000000; } // 1.32 fix FIJI edge case if (minLonMicroDeg <= lonMicroDeg && lonMicroDeg < maxLonMicroDeg) { @@ -381,17 +384,17 @@ static int isInRange(int lonMicroDeg, const int minLonMicroDeg, const int maxLon // returns true iff given coordinate "coord32" fits inside given TerritoryBoundary -static int fitsInsideBoundaries(const Point32 *coord32, const TerritoryBoundary *b) { +static int fitsInsideBoundaries(const Point32* coord32, const TerritoryBoundary* b) { ASSERT(coord32); ASSERT(b); return (b->miny <= coord32->latMicroDeg && - coord32->latMicroDeg < b->maxy && - isInRange(coord32->lonMicroDeg, b->minx, b->maxx)); + coord32->latMicroDeg < b->maxy && + isInRange(coord32->lonMicroDeg, b->minx, b->maxx)); } // set target TerritoryBoundary to a source extended with deltalat, deltaLon (in microDegrees) -static TerritoryBoundary *getExtendedBoundaries(TerritoryBoundary *target, const TerritoryBoundary *source, +static TerritoryBoundary* getExtendedBoundaries(TerritoryBoundary* target, const TerritoryBoundary* source, const int deltaLatMicroDeg, const int deltaLonMicroDeg) { ASSERT(target); ASSERT(source); @@ -419,7 +422,7 @@ typedef struct { } MapcodeZone; -static void setFromFractions(MapcodeZone *z, +static void setFromFractions(MapcodeZone* z, const double y, const double x, const double yDelta, const double xDelta) { ASSERT(z); @@ -427,21 +430,22 @@ static void setFromFractions(MapcodeZone *z, z->fmaxx = x + xDelta; if (yDelta < 0) { z->fminy = y + 1 + yDelta; // y+yDelta can NOT be represented - z->fmaxy = y + 1; // y CAN be represented - } else { + z->fmaxy = y + 1; // y CAN be represented + } + else { z->fminy = y; z->fmaxy = y + yDelta; } } -static int isEmpty(const MapcodeZone *z) { +static int isEmpty(const MapcodeZone* z) { ASSERT(z); return ((z->fmaxx <= z->fminx) || (z->fmaxy <= z->fminy)); } -static Point getMidPointFractions(const MapcodeZone *z) { +static Point getMidPointFractions(const MapcodeZone* z) { Point p; ASSERT(z); p.lon = floor((z->fminx + z->fmaxx) / 2); @@ -450,7 +454,7 @@ static Point getMidPointFractions(const MapcodeZone *z) { } -static void zoneCopyFrom(MapcodeZone *target, const MapcodeZone *source) { +static void zoneCopyFrom(MapcodeZone* target, const MapcodeZone* source) { ASSERT(target); ASSERT(source); target->fminy = source->fminy; @@ -462,7 +466,7 @@ static void zoneCopyFrom(MapcodeZone *target, const MapcodeZone *source) { // determine the non-empty intersection zone z between a given zone and the boundary of territory rectangle m. // returns nonzero in case such a zone exists -static int restrictZoneTo(MapcodeZone *z, const MapcodeZone *zone, const TerritoryBoundary *b) { +static int restrictZoneTo(MapcodeZone* z, const MapcodeZone* zone, const TerritoryBoundary* b) { ASSERT(z); ASSERT(zone); ASSERT(b); @@ -482,7 +486,8 @@ static int restrictZoneTo(MapcodeZone *z, const MapcodeZone *zone, const Territo if (bmaxx < 0 && z->fminx > 0) { bminx += (360000000 * MICROLON_TO_FRACTIONS_FACTOR); bmaxx += (360000000 * MICROLON_TO_FRACTIONS_FACTOR); - } else if (bminx > 0 && z->fmaxx < 0) { + } + else if (bminx > 0 && z->fmaxx < 0) { bminx -= (360000000 * MICROLON_TO_FRACTIONS_FACTOR); bmaxx -= (360000000 * MICROLON_TO_FRACTIONS_FACTOR); } @@ -504,19 +509,19 @@ static int restrictZoneTo(MapcodeZone *z, const MapcodeZone *zone, const Territo /////////////////////////////////////////////////////////////////////////////////////////////// // PRIVATE - copy characters into targetString, limited to its size -static char *lengthCopy(char *targetString, const char *sourceString, int nrCharacters, int targetSize) { +static char* lengthCopy(char* targetString, const char* sourceString, int nrCharacters, int targetSize) { if (nrCharacters >= targetSize) { nrCharacters = targetSize - 1; } - memcpy(targetString, sourceString, (size_t) nrCharacters); + memcpy(targetString, sourceString, (size_t)nrCharacters); targetString[nrCharacters] = 0; return targetString; } // PRIVATE - copy as much of sourceString as will fit; returns targetString -static char *safeCopy(char *targetString, const char *sourceString, const int targetSize) { - int sourceLength = (int) strlen(sourceString); +static char* safeCopy(char* targetString, const char* sourceString, const int targetSize) { + int sourceLength = (int)strlen(sourceString); return lengthCopy(targetString, sourceString, sourceLength, targetSize); } @@ -546,7 +551,7 @@ static enum Territory parentTerritoryOf(const enum Territory ccode) { if (ccode <= _TERRITORY_MIN || ccode >= _TERRITORY_MAX) { return TERRITORY_NONE; } - return PARENT_NR[(int) PARENT_LETTER[INDEX_OF_TERRITORY(ccode)]]; + return PARENT_NR[(int)PARENT_LETTER[INDEX_OF_TERRITORY(ccode)]]; } @@ -560,29 +565,31 @@ static int coDex(const int m) { static int xDivider4(const int miny, const int maxy) { // 360 * cos(microdegrees>>19) static const int xdivider19[172] = { - 360, 360, 360, 360, 360, 360, 361, 361, 361, 361, - 362, 362, 362, 363, 363, 363, 364, 364, 365, 366, - 366, 367, 367, 368, 369, 370, 370, 371, 372, 373, - 374, 375, 376, 377, 378, 379, 380, 382, 383, 384, - 386, 387, 388, 390, 391, 393, 394, 396, 398, 399, - 401, 403, 405, 407, 409, 411, 413, 415, 417, 420, - 422, 424, 427, 429, 432, 435, 437, 440, 443, 446, - 449, 452, 455, 459, 462, 465, 469, 473, 476, 480, - 484, 488, 492, 496, 501, 505, 510, 515, 520, 525, - 530, 535, 540, 546, 552, 558, 564, 570, 577, 583, - 590, 598, 605, 612, 620, 628, 637, 645, 654, 664, - 673, 683, 693, 704, 715, 726, 738, 751, 763, 777, - 791, 805, 820, 836, 852, 869, 887, 906, 925, 946, - 968, 990, 1014, 1039, 1066, 1094, 1123, 1154, 1187, 1223, - 1260, 1300, 1343, 1389, 1438, 1490, 1547, 1609, 1676, 1749, - 1828, 1916, 2012, 2118, 2237, 2370, 2521, 2691, 2887, 3114, - 3380, 3696, 4077, 4547, 5139, 5910, 6952, 8443, 10747, 14784, - 23681, 59485 + 360, 360, 360, 360, 360, 360, 361, 361, 361, 361, + 362, 362, 362, 363, 363, 363, 364, 364, 365, 366, + 366, 367, 367, 368, 369, 370, 370, 371, 372, 373, + 374, 375, 376, 377, 378, 379, 380, 382, 383, 384, + 386, 387, 388, 390, 391, 393, 394, 396, 398, 399, + 401, 403, 405, 407, 409, 411, 413, 415, 417, 420, + 422, 424, 427, 429, 432, 435, 437, 440, 443, 446, + 449, 452, 455, 459, 462, 465, 469, 473, 476, 480, + 484, 488, 492, 496, 501, 505, 510, 515, 520, 525, + 530, 535, 540, 546, 552, 558, 564, 570, 577, 583, + 590, 598, 605, 612, 620, 628, 637, 645, 654, 664, + 673, 683, 693, 704, 715, 726, 738, 751, 763, 777, + 791, 805, 820, 836, 852, 869, 887, 906, 925, 946, + 968, 990, 1014, 1039, 1066, 1094, 1123, 1154, 1187, 1223, + 1260, 1300, 1343, 1389, 1438, 1490, 1547, 1609, 1676, 1749, + 1828, 1916, 2012, 2118, 2237, 2370, 2521, 2691, 2887, 3114, + 3380, 3696, 4077, 4547, 5139, 5910, 6952, 8443, 10747, 14784, + 23681, 59485 }; - if (miny >= 0) { // both above equator? then miny is closest + if (miny >= 0) { + // both above equator? then miny is closest return xdivider19[(miny) >> 19]; } - if (maxy >= 0) { // opposite sides? then equator is worst + if (maxy >= 0) { + // opposite sides? then equator is worst return xdivider19[0]; } return xdivider19[(-maxy) >> 19]; // both negative, so maxy is closest to equator @@ -625,29 +632,29 @@ static int countNamelessRecords(const int m, const int firstcode) { } -static int isNearBorderOf(const Point32 *coord32, const TerritoryBoundary *b) { +static int isNearBorderOf(const Point32* coord32, const TerritoryBoundary* b) { int xdiv8 = xDivider4(b->miny, b->maxy) / 4; // should be /8 but there's some extra margin TerritoryBoundary tmp; ASSERT(coord32); ASSERT(b); return (fitsInsideBoundaries(coord32, getExtendedBoundaries(&tmp, b, +60, +xdiv8)) && - (!fitsInsideBoundaries(coord32, getExtendedBoundaries(&tmp, b, -60, -xdiv8)))); + (!fitsInsideBoundaries(coord32, getExtendedBoundaries(&tmp, b, -60, -xdiv8)))); } -static void makeUppercase(char *s) { +static void makeUppercase(char* s) { ASSERT(s); while (*s) { - *s = (char) toupper(*s); + *s = (char)toupper(*s); s++; } } // returns 1 - 8, or negative if error -static int getParentNumber(const char *s, const int len) { - const char *p = ((len == 2) ? PARENTS_2 : PARENTS_3); - const char *f; +static int getParentNumber(const char* s, const int len) { + const char* p = ((len == 2) ? PARENTS_2 : PARENTS_3); + const char* f; char country[4]; ASSERT(s[0] && s[1]); ASSERT((2 <= len) && (len <= 3)); @@ -658,7 +665,7 @@ static int getParentNumber(const char *s, const int len) { if (!f) { return -1; } - return 1 + (int) ((f - p) / (len + 1)); + return 1 + (int)((f - p) / (len + 1)); } /////////////////////////////////////////////////////////////////////////////////////////////// @@ -667,17 +674,18 @@ static int getParentNumber(const char *s, const int len) { // /////////////////////////////////////////////////////////////////////////////////////////////// -static void repackIfAllDigits(char *input, const int aonly) { - char *s = input; +static void repackIfAllDigits(char* input, const int aonly) { + char* s = input; int alldigits = 1; // assume all digits - char *e; - char *dotpos = NULL; + char* e; + char* dotpos = NULL; ASSERT(input); for (e = s; *e != 0 && *e != '-'; e++) { if (*e < '0' || *e > '9') { if (*e == '.' && !dotpos) { dotpos = e; - } else { + } + else { alldigits = 0; break; } @@ -694,7 +702,8 @@ static void repackIfAllDigits(char *input, const int aonly) { *input = 'A'; *s = ENCODE_CHARS[v / 32]; *e = ENCODE_CHARS[v % 32]; - } else // encode using A,E,U + } + else // encode using A,E,U { const int v = ((*s) - '0') * 10 + ((*e) - '0'); *s = ENCODE_CHARS[(v / 34) + 31]; @@ -706,9 +715,9 @@ static void repackIfAllDigits(char *input, const int aonly) { // rewrite all-digit codes // returns 1 if unpacked, 0 if left unchanged, negative if unchanged and an error was detected -static int unpackIfAllDigits(char *input) { - char *s = input; - char *dotpos = NULL; +static int unpackIfAllDigits(char* input) { + char* s = input; + char* dotpos = NULL; const int aonly = ((*s == 'A') || (*s == 'a')); if (aonly) { s++; @@ -716,52 +725,60 @@ static int unpackIfAllDigits(char *input) { for (; *s != 0 && s[2] != 0 && s[2] != '-'; s++) { if (*s == '-') { break; - } else if (*s == '.' && !dotpos) { + } + else if (*s == '.' && !dotpos) { dotpos = s; - } else if ((decodeChar(*s) < 0) || (decodeChar(*s) > 9)) { + } + else if ((decodeChar(*s) < 0) || (decodeChar(*s) > 9)) { return 0; - } // nondigit, so stop + } // nondigit, so stop } if (dotpos) { if (aonly) // v1.50 encoded only with A's { const int v = (((s[0] == 'A') || (s[0] == 'a')) ? 31 : decodeChar(s[0])) * 32 + - (((s[1] == 'A') || (s[1] == 'a')) ? 31 : decodeChar(s[1])); - *input = (char) ('0' + (v / 100)); - s[0] = (char) ('0' + ((v / 10) % 10)); - s[1] = (char) ('0' + (v % 10)); + (((s[1] == 'A') || (s[1] == 'a')) ? 31 : decodeChar(s[1])); + *input = (char)('0' + (v / 100)); + s[0] = (char)('0' + ((v / 10) % 10)); + s[1] = (char)('0' + (v % 10)); return 1; } // v1.50 if ((*s == 'a') || (*s == 'e') || (*s == 'u') || (*s == 'A') || (*s == 'E') || (*s == 'U')) { - char *e = s + 1; // s is vowel, e is lastchar + char* e = s + 1; // s is vowel, e is lastchar int v = 0; if (*s == 'e' || *s == 'E') { v = 34; - } else if (*s == 'u' || *s == 'U') { + } + else if (*s == 'u' || *s == 'U') { v = 68; } if ((*e == 'a') || (*e == 'A')) { v += 31; - } else if ((*e == 'e') || (*e == 'E')) { + } + else if ((*e == 'e') || (*e == 'E')) { v += 32; - } else if ((*e == 'u') || (*e == 'U')) { + } + else if ((*e == 'u') || (*e == 'U')) { v += 33; - } else if (decodeChar(*e) < 0) { - return (int) ERR_INVALID_CHARACTER; - } else { + } + else if (decodeChar(*e) < 0) { + return (int)ERR_INVALID_CHARACTER; + } + else { v += decodeChar(*e); } if (v < 100) { - *s = ENCODE_CHARS[(unsigned int) v / 10]; - *e = ENCODE_CHARS[(unsigned int) v % 10]; - } else { - return (int) ERR_INVALID_ENDVOWELS; // mapcodes ends in UE or UU + *s = ENCODE_CHARS[(unsigned int)v / 10]; + *e = ENCODE_CHARS[(unsigned int)v % 10]; + } + else { + return (int)ERR_INVALID_ENDVOWELS; // mapcodes ends in UE or UU } return 1; } @@ -781,33 +798,36 @@ typedef struct { int fraclat; // latitude fraction of microdegrees, expressed in 1 / 810,000ths int fraclon; // longitude fraction of microdegrees, expressed in 1 / 3,240,000ths // output - Mapcodes *mapcodes; + Mapcodes* mapcodes; } EncodeRec; // encode the high-precision extension (0-8 characters) -static void encodeExtension(char *result, const int extrax4, const int extray, const int dividerx4, +static void encodeExtension(char* result, const int extrax4, const int extray, const int dividerx4, const int dividery, int extraDigits, const int ydirection, - const EncodeRec *enc) // append extra characters to result for more precision + const EncodeRec* enc) // append extra characters to result for more precision { ASSERT(result); ASSERT(enc); - if (extraDigits > 0) { // anything to do? - char *s = result + strlen(result); - double factorx = (double) MAX_PRECISION_FACTOR * dividerx4; // perfect integer! - double factory = (double) MAX_PRECISION_FACTOR * dividery; // perfect integer! - double valx = ((double) MAX_PRECISION_FACTOR * extrax4) + enc->fraclon; // perfect integer! - double valy = ((double) MAX_PRECISION_FACTOR * extray) + (ydirection * enc->fraclat); // perfect integer! + if (extraDigits > 0) { + // anything to do? + char* s = result + strlen(result); + double factorx = (double)MAX_PRECISION_FACTOR * dividerx4; // perfect integer! + double factory = (double)MAX_PRECISION_FACTOR * dividery; // perfect integer! + double valx = ((double)MAX_PRECISION_FACTOR * extrax4) + enc->fraclon; // perfect integer! + double valy = ((double)MAX_PRECISION_FACTOR * extray) + (ydirection * enc->fraclat); // perfect integer! // protect against floating Point errors if (valx < 0) { valx = 0; - } else if (valx >= factorx) { + } + else if (valx >= factorx) { valx = factorx - 1; } if (valy < 0) { valy = 0; - } else if (valy >= factory) { + } + else if (valy >= factory) { valy = factory - 1; } @@ -821,10 +841,10 @@ static void encodeExtension(char *result, const int extrax4, const int extray, c int gx, gy; factorx /= 30; - gx = (int) (valx / factorx); + gx = (int)(valx / factorx); factory /= 30; - gy = (int) (valy / factory); + gy = (int)(valy / factory); *s++ = ENCODE_CHARS[(gy / 5) * 5 + (gx / 6)]; if (--extraDigits == 0) { @@ -846,7 +866,7 @@ static void encodeExtension(char *result, const int extrax4, const int extray, c // encode 'value' into result[nrchars] -static void encodeBase31(char *result, int value, int nrchars) { +static void encodeBase31(char* result, int value, int nrchars) { ASSERT(result); ASSERT(nrchars >= 0); result[nrchars] = 0; // zero-terminate! @@ -858,13 +878,14 @@ static void encodeBase31(char *result, int value, int nrchars) { } -static void encodeTriple(char *result, const int difx, const int dify) { +static void encodeTriple(char* result, const int difx, const int dify) { ASSERT(result); if (dify < 4 * 34) // first 4(x34) rows of 6(x28) wide { *result = ENCODE_CHARS[((difx / 28) + 6 * (dify / 34))]; encodeBase31(result + 1, ((difx % 28) * 34 + (dify % 34)), 2); - } else // bottom row + } + else // bottom row { *result = ENCODE_CHARS[(difx / 24) + 24]; encodeBase31(result + 1, (difx % 24) * 40 + (dify - 136), 2); @@ -894,9 +915,9 @@ static const int NC[6] = {1, 31, 961, 29791, 923521, 28629151}; // returns *result==0 in case of error -static void encodeGrid(char *result, const EncodeRec *enc, const int m, const int extraDigits, +static void encodeGrid(char* result, const EncodeRec* enc, const int m, const int extraDigits, const char headerLetter) { - const TerritoryBoundary *b = TERRITORY_BOUNDARY(m); + const TerritoryBoundary* b = TERRITORY_BOUNDARY(m); const int orgcodex = coDex(m); int codexm; ASSERT(result); @@ -906,7 +927,8 @@ static void encodeGrid(char *result, const EncodeRec *enc, const int m, const in codexm = orgcodex; if (codexm == 21) { codexm = 22; - } else if (codexm == 14) { + } + else if (codexm == 14) { codexm = 23; } @@ -915,7 +937,8 @@ static void encodeGrid(char *result, const EncodeRec *enc, const int m, const in result++; } - { // encode + { + // encode int divx, divy; const int prelen = codexm / 10; const int postlen = codexm % 10; @@ -925,11 +948,13 @@ static void encodeGrid(char *result, const EncodeRec *enc, const int m, const in if (divy == 1) { divx = X_SIDE[prelen]; divy = Y_SIDE[prelen]; - } else { + } + else { divx = (NC[prelen] / divy); } - { // grid + { + // grid const int ygridsize = (b->maxy - b->miny + divy - 1) / divy; const int xgridsize = (b->maxx - b->minx + divx - 1) / divx; int rely = enc->coord32.latMicroDeg - b->miny; @@ -939,7 +964,8 @@ static void encodeGrid(char *result, const EncodeRec *enc, const int m, const in if (relx < 0) { relx += 360000000; x += 360000000; - } else if (relx >= 360000000) // 1.32 fix FIJI edge case + } + else if (relx >= 360000000) // 1.32 fix FIJI edge case { relx -= 360000000; x -= 360000000; @@ -952,11 +978,13 @@ static void encodeGrid(char *result, const EncodeRec *enc, const int m, const in return; } - { // prefix + { + // prefix int v; if (divx != divy && prelen > 2) { v = encodeSixWide(relx, rely, divx, divy); - } else { + } + else { v = relx * divy + (divy - 1 - rely); } encodeBase31(result, v, prelen); @@ -971,13 +999,14 @@ static void encodeGrid(char *result, const EncodeRec *enc, const int m, const in rely = b->miny + (rely * ygridsize); relx = b->minx + (relx * xgridsize); - { // postfix + { + // postfix const int dividery = ((ygridsize + Y_SIDE[postlen] - 1) / Y_SIDE[postlen]); const int dividerx = ((xgridsize + X_SIDE[postlen] - 1) / X_SIDE[postlen]); int extrax, extray; { - char *resultptr = result + prelen; + char* resultptr = result + prelen; int difx = x - relx; @@ -997,7 +1026,8 @@ static void encodeGrid(char *result, const EncodeRec *enc, const int m, const in if (postlen == 3) // encode special { encodeTriple(resultptr, difx, dify); - } else { + } + else { encodeBase31(resultptr, (difx) * Y_SIDE[postlen] + dify, postlen); // swap 4-int codes for readability if (postlen == 4) { @@ -1020,12 +1050,12 @@ static void encodeGrid(char *result, const EncodeRec *enc, const int m, const in } } // postfix } // grid - } // encode + } // encode } // *result==0 in case of error -static void encodeNameless(char *result, const EncodeRec *enc, const enum Territory ccode, +static void encodeNameless(char* result, const EncodeRec* enc, const enum Territory ccode, const int extraDigits, const int m) { // determine how many nameless records there are (A), and which one is this (X)... const int A = countNamelessRecords(m, firstRec(ccode)); @@ -1045,27 +1075,31 @@ static void encodeNameless(char *result, const EncodeRec *enc, const enum Territ int SIDE; int storage_offset; - const TerritoryBoundary *b; + const TerritoryBoundary* b; int xSIDE, orgSIDE; if (codexm != 21 && A <= 31) { storage_offset = (X * p + (X < r ? X : r)) * (961 * 961); - } else if (codexm != 21 && A < 62) { + } + else if (codexm != 21 && A < 62) { if (X < (62 - A)) { storage_offset = X * (961 * 961); - } else { + } + else { storage_offset = (62 - A + ((X - 62 + A) / 2)) * (961 * 961); if ((X + A) & 1) { storage_offset += (16 * 961 * 31); } } - } else { + } + else { const int BASEPOWER = (codexm == 21) ? 961 * 961 : 961 * 961 * 31; int BASEPOWERA = (BASEPOWER / A); if (A == 62) { BASEPOWERA++; - } else { + } + else { BASEPOWERA = (961) * (BASEPOWERA / 961); } @@ -1099,7 +1133,8 @@ static void encodeNameless(char *result, const EncodeRec *enc, const enum Territ SIDE = 1 + ((b->maxy - b->miny) / 90); // new side, based purely on y-distance xSIDE = (orgSIDE * orgSIDE) / SIDE; v += encodeSixWide(dx, SIDE - 1 - dy, xSIDE, SIDE); - } else { + } + else { v += (dx * SIDE + dy); } @@ -1124,18 +1159,17 @@ static void encodeNameless(char *result, const EncodeRec *enc, const enum Territ encodeExtension(result, extrax4, extray, dividerx4, dividery, extraDigits, -1, enc); // nameless return; - } // in range } } // encode in m (known to fit) -static void encodeAutoHeader(char *result, const EncodeRec *enc, const int m, const int extraDigits) { +static void encodeAutoHeader(char* result, const EncodeRec* enc, const int m, const int extraDigits) { int i; int STORAGE_START = 0; int W, H, xdiv, product; - const TerritoryBoundary *b; + const TerritoryBoundary* b; // search back to first of the group int firstindex = m; @@ -1161,7 +1195,8 @@ static void encodeAutoHeader(char *result, const EncodeRec *enc, const int m, co H = 176 * ((H + 176 - 1) / 176); W = 168 * ((W + 168 - 1) / 168); product = (W / 168) * (H / 176) * 961 * 31; - if (REC_TYPE(i) == 2) { // plus pipe + if (REC_TYPE(i) == 2) { + // plus pipe const int GOODROUNDER = codexm >= 23 ? (961 * 961 * 31) : (961 * 961); product = ((STORAGE_START + product + GOODROUNDER - 1) / GOODROUNDER) * GOODROUNDER - STORAGE_START; } @@ -1199,7 +1234,7 @@ static void encodeAutoHeader(char *result, const EncodeRec *enc, const int m, co } -static void encoderEngine(const enum Territory ccode, const EncodeRec *enc, const int stop_with_one_result, +static void encoderEngine(const enum Territory ccode, const EncodeRec* enc, const int stop_with_one_result, const int extraDigits, const int requiredEncoder, const enum Territory ccode_override) { int from; int upto; @@ -1230,19 +1265,22 @@ static void encoderEngine(const enum Territory ccode, const EncodeRec *enc, cons if (fitsInsideBoundaries(&enc->coord32, TERRITORY_BOUNDARY(i))) { if (IS_NAMELESS(i)) { encodeNameless(result, enc, ccode, extraDigits, i); - } else if (REC_TYPE(i) > 1) { + } + else if (REC_TYPE(i) > 1) { encodeAutoHeader(result, enc, i, extraDigits); - } else if ((i == upto) && isSubdivision(ccode)) { + } + else if ((i == upto) && isSubdivision(ccode)) { // *** do a recursive call for the parent *** encoderEngine(parentTerritoryOf(ccode), enc, stop_with_one_result, extraDigits, requiredEncoder, ccode); return; - } else // must be grid + } + else // must be grid { // skip IS_RESTRICTED records unless there already is a result if (result_counter || !IS_RESTRICTED(i)) { if (coDex(i) < 54) { - char headerletter = (char) ((REC_TYPE(i) == 1) ? HEADER_LETTER(i) : 0); + char headerletter = (char)((REC_TYPE(i) == 1) ? HEADER_LETTER(i) : 0); encodeGrid(result, enc, i, extraDigits, headerletter); } } @@ -1257,10 +1295,12 @@ static void encoderEngine(const enum Territory ccode, const EncodeRec *enc, cons if ((requiredEncoder < 0) || (requiredEncoder == i)) { const enum Territory ccodeFinal = (ccode_override != TERRITORY_NONE ? ccode_override : ccode); if (*result && enc->mapcodes && (enc->mapcodes->count < MAX_NR_OF_MAPCODE_RESULTS)) { - char *s = enc->mapcodes->mapcode[enc->mapcodes->count++]; - if (ccodeFinal == TERRITORY_AAA) { // AAA is never shown with territory + char* s = enc->mapcodes->mapcode[enc->mapcodes->count++]; + if (ccodeFinal == TERRITORY_AAA) { + // AAA is never shown with territory strcpy(s, result); - } else { + } + else { getTerritoryIsoName(s, ccodeFinal, 0); strcat(s, " "); strcat(s, result); @@ -1283,7 +1323,7 @@ static void encoderEngine(const enum Territory ccode, const EncodeRec *enc, cons // pass Point to an array of pointers (at least 42), will be made to Point to result strings... // returns nr of results; -static int encodeLatLonToMapcodes_internal(Mapcodes *mapcodes, +static int encodeLatLonToMapcodes_internal(Mapcodes* mapcodes, const double lat, const double lon, const enum Territory territoryContext, const int stop_with_one_result, const int requiredEncoder, const int extraDigits) { @@ -1299,7 +1339,6 @@ static int encodeLatLonToMapcodes_internal(Mapcodes *mapcodes, if (territoryContext < _TERRITORY_MIN) // ALL results? { - #ifndef NO_FAST_ENCODE { const int sum = enc.coord32.lonMicroDeg + enc.coord32.latMicroDeg; @@ -1307,21 +1346,24 @@ static int encodeLatLonToMapcodes_internal(Mapcodes *mapcodes, int i = 0; // pointer into REDIVAR for (;;) { const int r = REDIVAR[i++]; - if (r >= 0 && r < 1024) { // leaf? + if (r >= 0 && r < 1024) { + // leaf? int j; for (j = 0; j <= r; j++) { - const enum Territory ccode = (j == r ? TERRITORY_AAA : (enum Territory) REDIVAR[i + j]); + const enum Territory ccode = (j == r ? TERRITORY_AAA : (enum Territory)REDIVAR[i + j]); encoderEngine(ccode, &enc, stop_with_one_result, extraDigits, requiredEncoder, TERRITORY_NONE); if ((stop_with_one_result || (requiredEncoder >= 0)) && (enc.mapcodes->count > 0)) { break; } } break; - } else { + } + else { coord = sum - coord; if (coord > r) { i = REDIVAR[i]; - } else { + } + else { i++; } } @@ -1331,15 +1373,16 @@ static int encodeLatLonToMapcodes_internal(Mapcodes *mapcodes, { int i; for (i = _TERRITORY_MIN + 1; i < _TERRITORY_MAX; i++) { - encoderEngine((enum Territory) i, &enc, stop_with_one_result, extraDigits, requiredEncoder, TERRITORY_NONE); + encoderEngine((enum Territory)i, &enc, stop_with_one_result, extraDigits, requiredEncoder, + TERRITORY_NONE); if ((stop_with_one_result || (requiredEncoder >= 0)) && (enc.mapcodes->count > 0)) { break; } } } #endif - - } else { + } + else { encoderEngine(territoryContext, &enc, stop_with_one_result, extraDigits, requiredEncoder, TERRITORY_NONE); } return mapcodes->count; @@ -1354,27 +1397,27 @@ static int encodeLatLonToMapcodes_internal(Mapcodes *mapcodes, typedef struct { // input MapcodeElements mapcodeElements; - const char *orginput; // original full input string - const char *mapcode; // input mapcode (first character of proper mapcode excluding territory code) - const char *extension; // input extension (or empty) + const char* orginput; // original full input string + const char* mapcode; // input mapcode (first character of proper mapcode excluding territory code) + const char* extension; // input extension (or empty) enum Territory context; // input territory context (or TERRITORY_NONE) - const char *iso; // input territory alphacode (context) + const char* iso; // input territory alphacode (context) // output - Point result; // result - Point32 coord32; // result in integer arithmetic (microdegrees) - MapcodeZone zone; // result zone (in "DegreeFractions") + Point result; // result + Point32 coord32; // result in integer arithmetic (microdegrees) + MapcodeZone zone; // result zone (in "DegreeFractions") } DecodeRec; // decode the high-precision extension (0-8 characters) -// this routine takes the integer-arithmeteic decoding results (dec->coord32), adds precision, +// this routine takes the integer-arithmeteic decoding results (dec->coord32), adds precision, // and determines result zone (dec->zone); returns negative in case of error. -static enum MapcodeError decodeExtension(DecodeRec *dec, +static enum MapcodeError decodeExtension(DecodeRec* dec, int dividerx4, int dividery, const int lon_offset4, const int extremeLat32, const int maxLon32) { double lat1, lon4; - const char *extrapostfix = dec->extension; + const char* extrapostfix = dec->extension; int lon32 = 0; int lat32 = 0; int processor = 1; @@ -1398,7 +1441,8 @@ static enum MapcodeError decodeExtension(DecodeRec *dec, } // illegal extension character row2 = (c2 / 6); column2 = (c2 % 6); - } else { + } + else { odd = 1; row2 = 0; column2 = 0; @@ -1415,14 +1459,15 @@ static enum MapcodeError decodeExtension(DecodeRec *dec, processor *= 30; } - lon4 = (dec->coord32.lonMicroDeg * 4 * (double) MAX_PRECISION_FACTOR) + ((lon32 * (double) dividerx4)) + - (lon_offset4 * (double) MAX_PRECISION_FACTOR); - lat1 = (dec->coord32.latMicroDeg * (double) MAX_PRECISION_FACTOR) + ((lat32 * (double) dividery)); + lon4 = (dec->coord32.lonMicroDeg * 4 * (double)MAX_PRECISION_FACTOR) + ((lon32 * (double)dividerx4)) + + (lon_offset4 * (double)MAX_PRECISION_FACTOR); + lat1 = (dec->coord32.latMicroDeg * (double)MAX_PRECISION_FACTOR) + ((lat32 * (double)dividery)); // determine the range of coordinates that are encoded to this mapcode if (odd) { setFromFractions(&dec->zone, lat1, lon4, 5 * dividery, 6 * dividerx4); - } else { + } + else { setFromFractions(&dec->zone, lat1, lon4, dividery, dividerx4); } @@ -1434,7 +1479,8 @@ static enum MapcodeError decodeExtension(DecodeRec *dec, if (dec->zone.fmaxy > extremeLat32 * MICROLAT_TO_FRACTIONS_FACTOR) { dec->zone.fmaxy = extremeLat32 * MICROLAT_TO_FRACTIONS_FACTOR; } - } else { + } + else { if (dec->zone.fminy < extremeLat32 * MICROLAT_TO_FRACTIONS_FACTOR) { dec->zone.fminy = extremeLat32 * MICROLAT_TO_FRACTIONS_FACTOR; } @@ -1447,7 +1493,7 @@ static enum MapcodeError decodeExtension(DecodeRec *dec, // decode 'code' until either a dot or an end-of-string is encountered -static int decodeBase31(const char *code) { +static int decodeBase31(const char* code) { int value = 0; ASSERT(code); while (*code != '.' && *code != 0) { @@ -1457,7 +1503,7 @@ static int decodeBase31(const char *code) { } -static void decodeTriple(const char *result, int *difx, int *dify) { +static void decodeTriple(const char* result, int* difx, int* dify) { // decode the first character const int c1 = decodeChar(*result++); ASSERT(result); @@ -1467,7 +1513,8 @@ static void decodeTriple(const char *result, int *difx, int *dify) { int m = decodeBase31(result); *difx = (c1 % 6) * 28 + (m / 34); *dify = (c1 / 6) * 34 + (m % 34); - } else // bottom row + } + else // bottom row { int x = decodeBase31(result); *dify = (x % 40) + 136; @@ -1476,7 +1523,7 @@ static void decodeTriple(const char *result, int *difx, int *dify) { } // decodeTriple static void decodeSixWide(const int v, const int width, const int height, - int *x, int *y) { + int* x, int* y) { int w; int D = 6; int col = v / (height * 6); @@ -1496,10 +1543,10 @@ static void decodeSixWide(const int v, const int width, const int height, // *** mid-level encode routines *** // decodes dec->mapcode in context of territory rectangle m; returns negative if error -static enum MapcodeError decodeGrid(DecodeRec *dec, const int m, const int hasHeaderLetter) { - const char *input = (hasHeaderLetter ? dec->mapcode + 1 : dec->mapcode); - const int codexlen = (int) (strlen(input) - 1); - int prelen = (int) (strchr(input, '.') - input); +static enum MapcodeError decodeGrid(DecodeRec* dec, const int m, const int hasHeaderLetter) { + const char* input = (hasHeaderLetter ? dec->mapcode + 1 : dec->mapcode); + const int codexlen = (int)(strlen(input) - 1); + int prelen = (int)(strchr(input, '.') - input); char result[MAX_PROPER_MAPCODE_ASCII_LEN + 1]; ASSERT(dec); @@ -1527,7 +1574,8 @@ static enum MapcodeError decodeGrid(DecodeRec *dec, const int m, const int hasHe if (divy == 1) { divx = X_SIDE[prelen]; divy = Y_SIDE[prelen]; - } else { + } + else { divx = (NC[prelen] / divy); } @@ -1544,7 +1592,8 @@ static enum MapcodeError decodeGrid(DecodeRec *dec, const int m, const int hasHe if (divx != divy && prelen > 2) { // special grid, useful when prefix is 3 or more, and not a nice 961x961 decodeSixWide(v, divx, divy, &relx, &rely); - } else { + } + else { relx = (v / divy); rely = divy - 1 - (v % divy); } @@ -1555,7 +1604,7 @@ static enum MapcodeError decodeGrid(DecodeRec *dec, const int m, const int hasHe { - const TerritoryBoundary *b = TERRITORY_BOUNDARY(m); + const TerritoryBoundary* b = TERRITORY_BOUNDARY(m); const int ygridsize = (b->maxy - b->miny + divy - 1) / divy; // microdegrees per cell const int xgridsize = (b->maxx - b->minx + divx - 1) / divx; // microdegrees per cell @@ -1571,13 +1620,14 @@ static enum MapcodeError decodeGrid(DecodeRec *dec, const int m, const int hasHe // decoderelative { - char *r = result + prelen + 1; + char* r = result + prelen + 1; int difx, dify; if (postlen == 3) // decode special { decodeTriple(r, &difx, &dify); - } else { + } + else { if (postlen == 4) { char t = r[1]; r[1] = r[2]; @@ -1617,11 +1667,11 @@ static enum MapcodeError decodeGrid(DecodeRec *dec, const int m, const int hasHe // decodes dec->mapcode in context of territory rectangle m, territory dec->context // Returns negative in case of error -static enum MapcodeError decodeNameless(DecodeRec *dec, int m) { +static enum MapcodeError decodeNameless(DecodeRec* dec, int m) { int A, F; char input[8]; const int codexm = coDex(m); - const int codexlen = (int) (strlen(dec->mapcode) - 1); + const int codexlen = (int)(strlen(dec->mapcode) - 1); ASSERT(dec); ASSERT((0 <= m) && (m <= MAPCODE_BOUNDARY_MAX)); if (codexlen != 4 && codexlen != 5) { @@ -1646,7 +1696,7 @@ static enum MapcodeError decodeNameless(DecodeRec *dec, int m) { int swapletters = 0; int xSIDE; int X; - const TerritoryBoundary *b; + const TerritoryBoundary* b; // make copy of input, so we can swap around letters during the decoding char result[32]; @@ -1658,26 +1708,30 @@ static enum MapcodeError decodeNameless(DecodeRec *dec, int m) { if (offset < r * (p + 1)) { X = offset / (p + 1); - } else { + } + else { swapletters = ((p == 1) && (codexm == 22)); X = r + (offset - (r * (p + 1))) / p; } - } else if (codexm != 21 && A < 62) { - + } + else if (codexm != 21 && A < 62) { X = decodeChar(*result); if (X < (62 - A)) { swapletters = (codexm == 22); - } else { + } + else { X = X + (X - (62 - A)); } - } else // code==21 || A>=62 + } + else // code==21 || A>=62 { const int BASEPOWER = (codexm == 21) ? 961 * 961 : 961 * 961 * 31; int BASEPOWERA = (BASEPOWER / A); if (A == 62) { BASEPOWERA++; - } else { + } + else { BASEPOWERA = 961 * (BASEPOWERA / 961); } @@ -1700,7 +1754,8 @@ static enum MapcodeError decodeNameless(DecodeRec *dec, int m) { if (X > 0) { v -= (X * p + (X < r ? X : r)) * (961 * 961); } - } else if (codexm != 21 && A < 62) { + } + else if (codexm != 21 && A < 62) { v = decodeBase31(result + 1); if (X >= (62 - A)) { if (v >= (16 * 961 * 31)) { @@ -1729,7 +1784,8 @@ static enum MapcodeError decodeNameless(DecodeRec *dec, int m) { decodeSixWide(v, xSIDE, SIDE, &dx, &dy); dy = SIDE - 1 - dy; - } else { + } + else { dy = v % SIDE; dx = v / SIDE; } @@ -1755,10 +1811,10 @@ static enum MapcodeError decodeNameless(DecodeRec *dec, int m) { // decodes dec->mapcode in context of territory rectangle m or one of its mates -static enum MapcodeError decodeAutoHeader(DecodeRec *dec, int m) { - const char *input = dec->mapcode; +static enum MapcodeError decodeAutoHeader(DecodeRec* dec, int m) { + const char* input = dec->mapcode; const int codexm = coDex(m); - const char *dot = strchr(input, '.'); + const char* dot = strchr(input, '.'); int STORAGE_START = 0; int value; ASSERT(dec); @@ -1771,7 +1827,7 @@ static enum MapcodeError decodeAutoHeader(DecodeRec *dec, int m) { value *= (961 * 31); for (; coDex(m) == codexm && REC_TYPE(m) > 1; m++) { - const TerritoryBoundary *b = TERRITORY_BOUNDARY(m); + const TerritoryBoundary* b = TERRITORY_BOUNDARY(m); // determine how many cells int H = (b->maxy - b->miny + 89) / 90; // multiple of 10m const int xdiv = xDivider4(b->miny, b->maxy); @@ -1832,14 +1888,14 @@ static unsigned char getRomanVersionOf(UWORD w) { if (w > ROMAN_VERSION_MAX_CHAR || ROMAN_VERSION_OF[w >> 6] == NULL) { return '?'; } - return (unsigned char) ROMAN_VERSION_OF[w >> 6][w & 63]; + return (unsigned char)ROMAN_VERSION_OF[w >> 6][w & 63]; } -static void convertFromAbjad(char *s) { +static void convertFromAbjad(char* s) { int len, dot, form, c; - char *postfix = strchr(s, '-'); - dot = (int) (strchr(s, '.') - s); + char* postfix = strchr(s, '-'); + dot = (int)(strchr(s, '.') - s); if (dot < 2 || dot > 5) { return; } @@ -1849,65 +1905,71 @@ static void convertFromAbjad(char *s) { unpackIfAllDigits(s); - len = (int) strlen(s); + len = (int)strlen(s); form = (dot >= 2 && dot <= 5 ? dot * 10 + (len - dot - 1) : 0); if (form == 23) { c = decodeChar(s[3]) * 8 + (decodeChar(s[4]) - 18); if (c >= 0 && c < 31) { -// s[0] = s[0]; -// s[1] = s[1]; -// s[2] = '.'; + // s[0] = s[0]; + // s[1] = s[1]; + // s[2] = '.'; s[3] = ENCODE_CHARS[c]; s[4] = s[5]; s[5] = 0; } - } else if (form == 24) { + } + else if (form == 24) { c = decodeChar(s[3]) * 8 + (decodeChar(s[4]) - 18); if (c >= 0 && c < 63) { -// s[0] = s[0]; -// s[1] = s[1]; -// s[2] = '.'; + // s[0] = s[0]; + // s[1] = s[1]; + // s[2] = '.'; s[3] = '.'; s[4] = s[5]; s[5] = s[6]; s[6] = 0; if (c >= 32) { s[2] = ENCODE_CHARS[c - 32]; - } else { + } + else { s[3] = ENCODE_CHARS[c]; } } - } else if (form == 34) { + } + else if (form == 34) { c = (decodeChar(s[2]) * 10) + (decodeChar(s[5]) - 7); if (c >= 0 && c < 93) { -// s[0] = s[0]; -// s[1] = s[1]; + // s[0] = s[0]; + // s[1] = s[1]; s[2] = '.'; -// s[3] = '.'; -// s[4] = s[4]; + // s[3] = '.'; + // s[4] = s[4]; s[5] = s[6]; s[6] = s[7]; s[7] = 0; if (c < 31) { s[3] = ENCODE_CHARS[c]; - } else if (c < 62) { + } + else if (c < 62) { s[2] = ENCODE_CHARS[c - 31]; - } else { + } + else { s[2] = ENCODE_CHARS[c - 62]; s[3] = s[4]; s[4] = '.'; } } - } else if (form == 35) { + } + else if (form == 35) { c = (decodeChar(s[2]) * 8) + (decodeChar(s[6]) - 18); if (c >= 0 && c < 63) { -// s[0] = s[0]; -// s[1] = s[1]; -// s[3] = '.'; -// s[4] = s[4]; -// s[5] = s[5]; + // s[0] = s[0]; + // s[1] = s[1]; + // s[3] = '.'; + // s[4] = s[4]; + // s[5] = s[5]; s[6] = s[7]; s[7] = s[8]; s[8] = 0; @@ -1915,33 +1977,36 @@ static void convertFromAbjad(char *s) { s[2] = ENCODE_CHARS[c - 32]; s[3] = s[4]; s[4] = '.'; - } else { + } + else { s[2] = ENCODE_CHARS[c]; } } - } else if (form == 45) { + } + else if (form == 45) { c = (decodeChar(s[2]) * 100) + (decodeChar(s[5]) * 10) + (decodeChar(s[8]) - 39); if (c >= 0 && c < 961) { -// s[0] = s[0]; -// s[1] = s[1]; + // s[0] = s[0]; + // s[1] = s[1]; s[2] = ENCODE_CHARS[c / 31]; -// s[3] = s[3]; -// s[4] = '.'; + // s[3] = s[3]; + // s[4] = '.'; s[5] = s[6]; s[6] = s[7]; s[7] = s[9]; s[8] = ENCODE_CHARS[c % 31]; s[9] = 0; } - } else if (form == 55) { + } + else if (form == 55) { c = (decodeChar(s[2]) * 100) + (decodeChar(s[6]) * 10) + (decodeChar(s[9]) - 39); if (c >= 0 && c < 961) { -// s[0] = s[0]; -// s[1] = s[1]; + // s[0] = s[0]; + // s[1] = s[1]; s[2] = ENCODE_CHARS[c / 31]; -// s[3] = s[3]; -// s[4] = s[4]; -// s[5] = '.'; + // s[3] = s[3]; + // s[4] = s[4]; + // s[5] = '.'; s[6] = s[7]; s[7] = s[8]; s[8] = s[10]; @@ -1951,7 +2016,7 @@ static void convertFromAbjad(char *s) { } repackIfAllDigits(s, 0); if (postfix) { - len = (int) strlen(s); + len = (int)strlen(s); *postfix = '-'; memmove(s + len, postfix, strlen(postfix) + 1); } @@ -1970,19 +2035,19 @@ static void convertFromAbjad(char *s) { * otherwise returns the alphabet of the first different character * encountered, or negative (_ALPHABET_MIN) if it isn't recognized. */ -static enum Alphabet recognizeAlphabetUtf8(const char *utf8) { +static enum Alphabet recognizeAlphabetUtf8(const char* utf8) { ASSERT(utf8); while (*utf8 != 0) { - int c = (unsigned char) *utf8++; + int c = (unsigned char)*utf8++; if (c >= 0xC0) { enum Alphabet alphabet; - int c2 = (unsigned char) *utf8++; + int c2 = (unsigned char)*utf8++; if (c2 < 0x80) { return _ALPHABET_MIN; // utf8 error! } c = ((c - 0xC0) << 6) + (c2 & 63); if (c >= 0x800) { - int c3 = (unsigned char) *utf8++; + int c3 = (unsigned char)*utf8++; c = ((c - 0x800) << 6) + (c3 & 63); if (c3 < 0x80 || c > 0xFFFF) { return _ALPHABET_MIN; // utf8 error! @@ -2005,107 +2070,156 @@ static enum Alphabet recognizeAlphabetUtf8(const char *utf8) { /////////////////////////////////////////////////////////////////////////////////////////////// -// 32=busyextension 64=end territory 128(256)=end of clean mapcode(with extension) 512=end of extension +// 32=busyextension 64=end territory 128(256)=end of clean mapcode(with extension) 512=end of extension static const int STATE_MACHINE[27][6] = { - // SPACE DOT DETTER VOWEL ZERO HYPHEN - // 0 start === looking for very first detter - {0, ERR_UNEXPECTED_DOT, 1, 1, ERR_DOT_MISSING, ERR_UNEXPECTED_HYPHEN}, - // 1 L/P === det:LL vowel:TA - {ERR_BAD_TERRITORY_FORMAT, ERR_UNEXPECTED_DOT, 2, 23, ERR_DOT_MISSING, ERR_BAD_TERRITORY_FORMAT}, - // 2 LL/PP === white: TT waitprefix | dot: PP. | det:LLL/PPP | vowel:TTA | hyphen:TT- - {18 | - 64, 6, 3, 24, ERR_DOT_MISSING, 14}, - // 3 LLL/PPP === white: TTT prefix | dot: PPP. mapcode | det: PPPP | hyphen: TTT- - {18 | - 64, 6, 4, ERR_INVALID_VOWEL, ERR_DOT_MISSING, 14}, - // 4 PPPP === dot: PPPP. | det: PPPPP - {ERR_BAD_TERRITORY_FORMAT, 6, 5, ERR_INVALID_VOWEL, ERR_DOT_MISSING, ERR_BAD_TERRITORY_FORMAT}, - // 5 PPPPP === must get dot now! Dot:PPPPP. - {ERR_BAD_TERRITORY_FORMAT, 6, ERR_INVALID_MAPCODE_FORMAT, ERR_INVALID_VOWEL, ERR_DOT_MISSING, ERR_BAD_TERRITORY_FORMAT}, - // 6 prefix. === get first postfix! det: prefix.L | vowel: prefix.A - {ERR_INVALID_MAPCODE_FORMAT, ERR_UNEXPECTED_DOT, 7, 25, ERR_MAPCODE_INCOMPLETE, ERR_UNEXPECTED_HYPHEN}, - // 7 prefix.L === get 2nd postfix! det: prefix.LL | vowel: prefix.LA - {ERR_INVALID_MAPCODE_FORMAT, ERR_UNEXPECTED_DOT, 8, 25, ERR_MAPCODE_INCOMPLETE, ERR_UNEXPECTED_HYPHEN}, - // 8 prefix.LL === get 3d postfix! white:trail | det: prefix.LLL | vowel: prefix.LLA | zero:done | hyphen: mc- - {22 | 128, ERR_UNEXPECTED_DOT, 9, 25, STATE_GO | - 128, - 11 | - 256}, - // 9 prefix.LLL === white:trail | zero:done | hyphen:mc- - {22 | - 128, ERR_UNEXPECTED_DOT, 10, 25, STATE_GO | - 128, 11 | - 256}, - //10 prefix.LLLL === white:trail | zero:done | hyphen:mc- | det/vowel = postfix full - {22 | - 128, ERR_UNEXPECTED_DOT, 13, 13, STATE_GO | - 128, 11 | - 256}, - - //11 mc- === MUST get first precision detter - {ERR_EXTENSION_INVALID_LENGTH, ERR_UNEXPECTED_DOT, 12, ERR_EXTENSION_INVALID_CHARACTER, ERR_MAPCODE_INCOMPLETE, ERR_UNEXPECTED_HYPHEN}, - //12 mc-L* === Keep reading precision detters | white=trail | zero=done - {22 | 512, ERR_UNEXPECTED_DOT, 12 | 32, ERR_EXTENSION_INVALID_CHARACTER, - STATE_GO | - 512, ERR_UNEXPECTED_HYPHEN}, - - //13 prefix.LLLLL === - {22 | - 128, ERR_UNEXPECTED_DOT, ERR_INVALID_MAPCODE_FORMAT, ERR_INVALID_VOWEL, STATE_GO | - 128, 11 | - 256}, - - //14 TC- === get first state letter - {ERR_BAD_TERRITORY_FORMAT, ERR_UNEXPECTED_DOT, 15, 15, ERR_BAD_TERRITORY_FORMAT, ERR_UNEXPECTED_HYPHEN}, - //15 TC-S === get 2nd state letter - {ERR_BAD_TERRITORY_FORMAT, ERR_UNEXPECTED_DOT, 16, 16, ERR_BAD_TERRITORY_FORMAT, ERR_UNEXPECTED_HYPHEN}, - //16 TC-SS === white:waitprefix | det/vow:TC-SSS - {18 | - 64, ERR_UNEXPECTED_DOT, 17, 17, ERR_DOT_MISSING, ERR_UNEXPECTED_HYPHEN}, - //17 TC-SSS === white:waitprefix - {18 | - 64, ERR_UNEXPECTED_DOT, ERR_BAD_TERRITORY_FORMAT, ERR_BAD_TERRITORY_FORMAT, ERR_DOT_MISSING, ERR_UNEXPECTED_HYPHEN}, - - //18 TC waitprefix === skip more whitespace, MUST get 1st prefix letter/vowel - {18, ERR_UNEXPECTED_DOT, 19, 19, ERR_DOT_MISSING, ERR_UNEXPECTED_HYPHEN}, - //19 TC P === get second prefix detter - {ERR_DOT_MISSING, ERR_UNEXPECTED_DOT, 20, ERR_INVALID_VOWEL, ERR_DOT_MISSING, ERR_UNEXPECTED_HYPHEN}, - //20 TC PP === dot:prefix. | det:TC PPP - {ERR_DOT_MISSING, 6, 21, ERR_INVALID_VOWEL, ERR_DOT_MISSING, ERR_UNEXPECTED_HYPHEN}, - //21 TC PPP === dot:prefix. | det:PPPP - {ERR_DOT_MISSING, 6, 4, ERR_INVALID_VOWEL, ERR_DOT_MISSING, ERR_UNEXPECTED_HYPHEN}, - - //22 trailing === skip whitespace until end of string - {22, ERR_UNEXPECTED_DOT, ERR_TRAILING_CHARACTERS, ERR_TRAILING_CHARACTERS, STATE_GO, ERR_UNEXPECTED_HYPHEN}, - - //23 TA === white:waitprefix | det: TAT | vowel:TAA | hyphen:TC- - {18 | - 64, ERR_INVALID_VOWEL, 24, 24, ERR_DOT_MISSING, 14}, - //24 TTA/TAT/TAA === space:TC waitprefix | hyphen:TC- - {18 | - 64, ERR_INVALID_VOWEL, ERR_INVALID_VOWEL, ERR_INVALID_VOWEL, ERR_DOT_MISSING, 14}, - - //25 prefix.[L*]A === white:trail | det/vow:full mc | zero:done | hyphen:mc- - {22 | - 128, ERR_UNEXPECTED_DOT, 26, 26, STATE_GO | - 128, 11 | - 256}, - //26 prefix.[L*]AL === white:trail | zero:done | hyphen:mc- - {22 | - 128, ERR_UNEXPECTED_DOT, ERR_INVALID_VOWEL, ERR_INVALID_VOWEL, STATE_GO | - 128, 11 | - 256}, + // SPACE DOT DETTER VOWEL ZERO HYPHEN + // 0 start === looking for very first detter + {0, ERR_UNEXPECTED_DOT, 1, 1, ERR_DOT_MISSING, ERR_UNEXPECTED_HYPHEN}, + // 1 L/P === det:LL vowel:TA + {ERR_BAD_TERRITORY_FORMAT, ERR_UNEXPECTED_DOT, 2, 23, ERR_DOT_MISSING, ERR_BAD_TERRITORY_FORMAT}, + // 2 LL/PP === white: TT waitprefix | dot: PP. | det:LLL/PPP | vowel:TTA | hyphen:TT- + { + 18 | + 64, + 6, 3, 24, ERR_DOT_MISSING, 14 + }, + // 3 LLL/PPP === white: TTT prefix | dot: PPP. mapcode | det: PPPP | hyphen: TTT- + { + 18 | + 64, + 6, 4, ERR_INVALID_VOWEL, ERR_DOT_MISSING, 14 + }, + // 4 PPPP === dot: PPPP. | det: PPPPP + {ERR_BAD_TERRITORY_FORMAT, 6, 5, ERR_INVALID_VOWEL, ERR_DOT_MISSING, ERR_BAD_TERRITORY_FORMAT}, + // 5 PPPPP === must get dot now! Dot:PPPPP. + { + ERR_BAD_TERRITORY_FORMAT, 6, ERR_INVALID_MAPCODE_FORMAT, ERR_INVALID_VOWEL, ERR_DOT_MISSING, + ERR_BAD_TERRITORY_FORMAT + }, + // 6 prefix. === get first postfix! det: prefix.L | vowel: prefix.A + {ERR_INVALID_MAPCODE_FORMAT, ERR_UNEXPECTED_DOT, 7, 25, ERR_MAPCODE_INCOMPLETE, ERR_UNEXPECTED_HYPHEN}, + // 7 prefix.L === get 2nd postfix! det: prefix.LL | vowel: prefix.LA + {ERR_INVALID_MAPCODE_FORMAT, ERR_UNEXPECTED_DOT, 8, 25, ERR_MAPCODE_INCOMPLETE, ERR_UNEXPECTED_HYPHEN}, + // 8 prefix.LL === get 3d postfix! white:trail | det: prefix.LLL | vowel: prefix.LLA | zero:done | hyphen: mc- + { + 22 | 128, ERR_UNEXPECTED_DOT, 9, 25, STATE_GO | + 128, + 11 | + 256 + }, + // 9 prefix.LLL === white:trail | zero:done | hyphen:mc- + { + 22 | + 128, + ERR_UNEXPECTED_DOT, 10, 25, STATE_GO | + 128, + 11 | + 256 + }, + //10 prefix.LLLL === white:trail | zero:done | hyphen:mc- | det/vowel = postfix full + { + 22 | + 128, + ERR_UNEXPECTED_DOT, 13, 13, STATE_GO | + 128, + 11 | + 256 + }, + + //11 mc- === MUST get first precision detter + { + ERR_EXTENSION_INVALID_LENGTH, ERR_UNEXPECTED_DOT, 12, ERR_EXTENSION_INVALID_CHARACTER, ERR_MAPCODE_INCOMPLETE, + ERR_UNEXPECTED_HYPHEN + }, + //12 mc-L* === Keep reading precision detters | white=trail | zero=done + { + 22 | 512, ERR_UNEXPECTED_DOT, 12 | 32, ERR_EXTENSION_INVALID_CHARACTER, + STATE_GO | + 512, + ERR_UNEXPECTED_HYPHEN + }, + + //13 prefix.LLLLL === + { + 22 | + 128, + ERR_UNEXPECTED_DOT, ERR_INVALID_MAPCODE_FORMAT, ERR_INVALID_VOWEL, STATE_GO | + 128, + 11 | + 256 + }, + + //14 TC- === get first state letter + {ERR_BAD_TERRITORY_FORMAT, ERR_UNEXPECTED_DOT, 15, 15, ERR_BAD_TERRITORY_FORMAT, ERR_UNEXPECTED_HYPHEN}, + //15 TC-S === get 2nd state letter + {ERR_BAD_TERRITORY_FORMAT, ERR_UNEXPECTED_DOT, 16, 16, ERR_BAD_TERRITORY_FORMAT, ERR_UNEXPECTED_HYPHEN}, + //16 TC-SS === white:waitprefix | det/vow:TC-SSS + { + 18 | + 64, + ERR_UNEXPECTED_DOT, 17, 17, ERR_DOT_MISSING, ERR_UNEXPECTED_HYPHEN + }, + //17 TC-SSS === white:waitprefix + { + 18 | + 64, + ERR_UNEXPECTED_DOT, ERR_BAD_TERRITORY_FORMAT, ERR_BAD_TERRITORY_FORMAT, ERR_DOT_MISSING, ERR_UNEXPECTED_HYPHEN + }, + + //18 TC waitprefix === skip more whitespace, MUST get 1st prefix letter/vowel + {18, ERR_UNEXPECTED_DOT, 19, 19, ERR_DOT_MISSING, ERR_UNEXPECTED_HYPHEN}, + //19 TC P === get second prefix detter + {ERR_DOT_MISSING, ERR_UNEXPECTED_DOT, 20, ERR_INVALID_VOWEL, ERR_DOT_MISSING, ERR_UNEXPECTED_HYPHEN}, + //20 TC PP === dot:prefix. | det:TC PPP + {ERR_DOT_MISSING, 6, 21, ERR_INVALID_VOWEL, ERR_DOT_MISSING, ERR_UNEXPECTED_HYPHEN}, + //21 TC PPP === dot:prefix. | det:PPPP + {ERR_DOT_MISSING, 6, 4, ERR_INVALID_VOWEL, ERR_DOT_MISSING, ERR_UNEXPECTED_HYPHEN}, + + //22 trailing === skip whitespace until end of string + {22, ERR_UNEXPECTED_DOT, ERR_TRAILING_CHARACTERS, ERR_TRAILING_CHARACTERS, STATE_GO, ERR_UNEXPECTED_HYPHEN}, + + //23 TA === white:waitprefix | det: TAT | vowel:TAA | hyphen:TC- + { + 18 | + 64, + ERR_INVALID_VOWEL, 24, 24, ERR_DOT_MISSING, 14 + }, + //24 TTA/TAT/TAA === space:TC waitprefix | hyphen:TC- + { + 18 | + 64, + ERR_INVALID_VOWEL, ERR_INVALID_VOWEL, ERR_INVALID_VOWEL, ERR_DOT_MISSING, 14 + }, + + //25 prefix.[L*]A === white:trail | det/vow:full mc | zero:done | hyphen:mc- + { + 22 | + 128, + ERR_UNEXPECTED_DOT, 26, 26, STATE_GO | + 128, + 11 | + 256 + }, + //26 prefix.[L*]AL === white:trail | zero:done | hyphen:mc- + { + 22 | + 128, + ERR_UNEXPECTED_DOT, ERR_INVALID_VOWEL, ERR_INVALID_VOWEL, STATE_GO | + 128, + 11 | + 256 + }, }; // Returns 0 if ok, negative in case of error (where -999 represents "may BECOME a valid mapcode if more characters are added) -static enum MapcodeError parseMapcodeString(MapcodeElements *mapcodeElements, const char *string, int interpretAsUtf16, +static enum MapcodeError parseMapcodeString(MapcodeElements* mapcodeElements, const char* string, int interpretAsUtf16, enum Territory territory) { - const UWORD *utf16 = (const UWORD *) string; + const UWORD* utf16 = (const UWORD*)string; int isAbjad = 0; - const unsigned char *utf8 = (unsigned char *) string; + const unsigned char* utf8 = (unsigned char*)string; int extensionLength = 0; - char *cleanPtr = NULL; + char* cleanPtr = NULL; int nondigits = 0, vowels = 0; int state = 0; ASSERT(string); @@ -2125,37 +2239,43 @@ static enum MapcodeError parseMapcodeString(MapcodeElements *mapcodeElements, co isAbjad = 1; } cx = getRomanVersionOf(*utf16++); - } else { + } + else { cx = *utf8++; } // recognize token: decode returns -2=a -3=e -4=0, 0..9 for digit or "o" or "i", 10..31 for char, -1 for illegal char if (cx == '.') { token = TOKENDOT; if (mapcodeElements) { - mapcodeElements->indexOfDot = (int) (cleanPtr - mapcodeElements->properMapcode); + mapcodeElements->indexOfDot = (int)(cleanPtr - mapcodeElements->properMapcode); } if (mapcodeElements) { *cleanPtr++ = cx; } - } else if (cx == '-') { + } + else if (cx == '-') { token = TOKENHYPH; if (mapcodeElements) { *cleanPtr++ = cx; } - } else if (cx == 0) { + } + else if (cx == 0) { token = TOKENZERO; - } else if ((cx == ' ') || (cx == '\t')) { + } + else if ((cx == ' ') || (cx == '\t')) { token = TOKENSEP; - } else { + } + else { signed char c; - if (cx >= 0xC0) { // utf8 character + if (cx >= 0xC0) { + // utf8 character unsigned char c2 = *utf8++; int w = ((cx - 0xC0) << 6) + (c2 & 63); if (c2 < 0x80) { return ERR_INVALID_CHARACTER; // utf8 error } if (w >= 0x800) { - int c3 = (int) *utf8++; + int c3 = (int)*utf8++; w = ((w - 0x800) << 6) + (c3 & 63); if (c3 < 0x80 || w > 0xFFFF) { return ERR_INVALID_CHARACTER; // utf8 error @@ -2168,42 +2288,51 @@ static enum MapcodeError parseMapcodeString(MapcodeElements *mapcodeElements, co isAbjad = 1; } } - cx = getRomanVersionOf((UWORD) w); + cx = getRomanVersionOf((UWORD)w); } c = decodeChar(cx); - if (c < 0) { // vowel or illegal? - if (c == -1) { // illegal? + if (c < 0) { + // vowel or illegal? + if (c == -1) { + // illegal? return ERR_INVALID_CHARACTER; } token = TOKENVOWEL; vowels++; if (mapcodeElements) { - *cleanPtr++ = (char) toupper(cx); + *cleanPtr++ = (char)toupper(cx); } - } else if (c < 10) { // digit + } + else if (c < 10) { + // digit token = TOKENCHR; // digit if (mapcodeElements) { - *cleanPtr++ = (char) toupper(cx); + *cleanPtr++ = (char)toupper(cx); } - } else { // character B-Z + } + else { + // character B-Z token = TOKENCHR; if (!extensionLength) { nondigits++; } if (mapcodeElements) { - *cleanPtr++ = (char) toupper(cx); + *cleanPtr++ = (char)toupper(cx); } } } newstate = STATE_MACHINE[state][token]; if (newstate >= 32) { - if (newstate >= 512) { // end of extension + if (newstate >= 512) { + // end of extension if (mapcodeElements) { *cleanPtr = 0; cleanPtr = mapcodeElements->precisionExtension; } - } else if (newstate >= 128) { - if (newstate >= 256) { // start of extension + } + else if (newstate >= 128) { + if (newstate >= 256) { + // start of extension extensionLength = 1; cleanPtr--; // get rid of hyphen } @@ -2212,15 +2341,20 @@ static enum MapcodeError parseMapcodeString(MapcodeElements *mapcodeElements, co *cleanPtr = 0; cleanPtr = mapcodeElements->precisionExtension; } - } else if (newstate >= 64) { // end of territory + } + else if (newstate >= 64) { + // end of territory nondigits = vowels = 0; if (mapcodeElements) { - int len = (int) (cleanPtr - mapcodeElements->properMapcode); + int len = (int)(cleanPtr - mapcodeElements->properMapcode); ASSERT(len < MAX_ISOCODE_ASCII_LEN); - lengthCopy(mapcodeElements->territoryISO, mapcodeElements->properMapcode, len, MAX_ISOCODE_ASCII_LEN + 1); + lengthCopy(mapcodeElements->territoryISO, mapcodeElements->properMapcode, len, + MAX_ISOCODE_ASCII_LEN + 1); cleanPtr = mapcodeElements->properMapcode; } - } else { // add to extension + } + else { + // add to extension if (++extensionLength > MAX_PRECISION_DIGITS) { return ERR_EXTENSION_INVALID_LENGTH; } @@ -2229,11 +2363,13 @@ static enum MapcodeError parseMapcodeString(MapcodeElements *mapcodeElements, co } if (newstate < 0) { - return (enum MapcodeError) newstate; - } else if (newstate == STATE_GO) { + return (enum MapcodeError)newstate; + } + else if (newstate == STATE_GO) { if (vowels > 3 || (nondigits == 1 && vowels > 1) || (nondigits > 1 && vowels > 0)) { return ERR_INVALID_VOWEL; - } else if (nondigits == 0 && vowels == 0) { + } + else if (nondigits == 0 && vowels == 0) { return ERR_ALL_DIGIT_CODE; } if (mapcodeElements) { @@ -2243,15 +2379,16 @@ static enum MapcodeError parseMapcodeString(MapcodeElements *mapcodeElements, co } if (isAbjad) { convertFromAbjad(mapcodeElements->properMapcode); - mapcodeElements->indexOfDot = (int) (strchr(mapcodeElements->properMapcode, '.') - - mapcodeElements->properMapcode); + mapcodeElements->indexOfDot = (int)(strchr(mapcodeElements->properMapcode, '.') - + mapcodeElements->properMapcode); } if (*mapcodeElements->territoryISO) { mapcodeElements->territoryCode = getTerritoryCode(mapcodeElements->territoryISO, territory); if (mapcodeElements->territoryCode < _TERRITORY_MIN) { return ERR_UNKNOWN_TERRITORY; } - } else { + } + else { mapcodeElements->territoryCode = territory; } if ((mapcodeElements->territoryCode == TERRITORY_MEX) && (strlen(mapcodeElements->properMapcode) < 8)) { @@ -2267,32 +2404,33 @@ static enum MapcodeError parseMapcodeString(MapcodeElements *mapcodeElements, co } -enum MapcodeError compareWithMapcodeFormatUtf8(const char *utf8String) { +enum MapcodeError compareWithMapcodeFormatUtf8(const char* utf8String) { ASSERT(utf8String); return parseMapcodeString(NULL, utf8String, FLAG_UTF8_STRING, TERRITORY_NONE); } -enum MapcodeError compareWithMapcodeFormatUtf16(const UWORD *Utf16String) { +enum MapcodeError compareWithMapcodeFormatUtf16(const UWORD* Utf16String) { ASSERT(Utf16String); - return parseMapcodeString(NULL, (const char *) Utf16String, FLAG_UTF16_STRING, TERRITORY_NONE); + return parseMapcodeString(NULL, (const char*)Utf16String, FLAG_UTF16_STRING, TERRITORY_NONE); } // returns nonzero if error -static enum MapcodeError decoderEngine(DecodeRec *dec, int parseFlags) { +static enum MapcodeError decoderEngine(DecodeRec* dec, int parseFlags) { enum Territory ccode; enum MapcodeError err; int codex; int from; int upto; int i; - char *s; + char* s; int wasAllDigits = 0; ASSERT(dec); err = parseMapcodeString(&dec->mapcodeElements, dec->orginput, parseFlags, dec->context); - if (err) { // clear all parsed fields in case of error + if (err) { + // clear all parsed fields in case of error dec->mapcodeElements.territoryISO[0] = 0; dec->mapcodeElements.properMapcode[0] = 0; dec->mapcodeElements.precisionExtension[0] = 0; @@ -2303,7 +2441,7 @@ static enum MapcodeError decoderEngine(DecodeRec *dec, int parseFlags) { dec->context = ccode; dec->mapcode = dec->mapcodeElements.properMapcode; dec->extension = dec->mapcodeElements.precisionExtension; - codex = dec->mapcodeElements.indexOfDot * 9 + (int) strlen(dec->mapcodeElements.properMapcode) - 1; + codex = dec->mapcodeElements.indexOfDot * 9 + (int)strlen(dec->mapcodeElements.properMapcode) - 1; s = dec->mapcodeElements.properMapcode; if (strchr(s, 'A') || strchr(s, 'E') || strchr(s, 'U')) { @@ -2316,12 +2454,15 @@ static enum MapcodeError decoderEngine(DecodeRec *dec, int parseFlags) { if (codex > 54) { ASSERT(codex == 55); return ERR_MAPCODE_UNDECODABLE; - } else if (codex == 54) { + } + else if (codex == 54) { // international mapcodes must be in international context ccode = TERRITORY_AAA; - } else if (ccode < _TERRITORY_MIN) { + } + else if (ccode < _TERRITORY_MIN) { return ERR_MISSING_TERRITORY; - } else if (isSubdivision(ccode)) { + } + else if (isSubdivision(ccode)) { // int mapcodes must be interpreted in the parent of a subdivision enum Territory parent = parentTerritoryOf(ccode); if ((codex == 44) || ((codex == 34 || codex == 43) && (parent == TERRITORY_IND || parent == TERRITORY_MEX))) { @@ -2345,7 +2486,8 @@ static enum MapcodeError decoderEngine(DecodeRec *dec, int parseFlags) { err = decodeNameless(dec, i); break; } - } else { + } + else { if ((codexi == codex) || ((codex == 22) && (codexi == 21))) { err = decodeGrid(dec, i, 0); @@ -2359,7 +2501,8 @@ static enum MapcodeError decoderEngine(DecodeRec *dec, int parseFlags) { // *** make sure decode fits somewhere *** dec->result = getMidPointFractions(&dec->zone); dec->coord32 = convertFractionsToCoord32(&dec->result); - for (j = i - 1; j >= from; j--) { // look in previous rects + for (j = i - 1; j >= from; j--) { + // look in previous rects if (!IS_RESTRICTED(j)) { if (fitsInsideBoundaries(&dec->coord32, TERRITORY_BOUNDARY(j))) { nrZoneOverlaps = 1; @@ -2371,7 +2514,8 @@ static enum MapcodeError decoderEngine(DecodeRec *dec, int parseFlags) { if (!nrZoneOverlaps) { MapcodeZone zfound; TerritoryBoundary prevu; - for (j = from; j < i; j++) { // try all smaller rectangles j + for (j = from; j < i; j++) { + // try all smaller rectangles j if (!IS_RESTRICTED(j)) { MapcodeZone z; if (restrictZoneTo(&z, &dec->zone, TERRITORY_BOUNDARY(j))) { @@ -2381,7 +2525,9 @@ static enum MapcodeError decoderEngine(DecodeRec *dec, int parseFlags) { zoneCopyFrom(&zfound, &z); ASSERT(j <= MAPCODE_BOUNDARY_MAX); memcpy(&prevu, TERRITORY_BOUNDARY(j), sizeof(TerritoryBoundary)); - } else { // nrZoneOverlaps >= 2 + } + else { + // nrZoneOverlaps >= 2 // more than one hit break; // give up } @@ -2391,7 +2537,8 @@ static enum MapcodeError decoderEngine(DecodeRec *dec, int parseFlags) { // if several sub-areas intersect, just return the whole zone // (the center of which may NOT re-encode to the same mapcode!) - if (nrZoneOverlaps == 1) { // found exactly ONE intersection? + if (nrZoneOverlaps == 1) { + // found exactly ONE intersection? zoneCopyFrom(&dec->zone, &zfound); } } @@ -2399,16 +2546,19 @@ static enum MapcodeError decoderEngine(DecodeRec *dec, int parseFlags) { if (!nrZoneOverlaps) { err = ERR_MAPCODE_UNDECODABLE; // type 3 "NLD L222.222" } - } // *** make sure decode fits somewhere *** + } // *** make sure decode fits somewhere *** break; } } - } else if (r == 1) { + } + else if (r == 1) { if (codex == codexi + 10 && HEADER_LETTER(i) == *s) { err = decodeGrid(dec, i, 1); break; } - } else { //r>1 + } + else { + //r>1 if (((codex == 23) && (codexi == 22)) || ((codex == 33) && (codexi == 23))) { err = decodeAutoHeader(dec, i); @@ -2460,36 +2610,149 @@ static enum MapcodeError decoderEngine(DecodeRec *dec, int parseFlags) { /////////////////////////////////////////////////////////////////////////////////////////////// // WARNING - these alphabets have NOT yet been released as standard! use at your own risk! check www.mapcode.com for details. -static const UWORD ASCII_TO_UTF16[_ALPHABET_MAX][36] = { // A-Z equivalents for ascii characters A to Z, 0-9 - // A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 - {0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039}, // roman - {0x0391, 0x0392, 0x039e, 0x0394, 0x0388, 0x0395, 0x0393, 0x0397, 0x0399, 0x03a0, 0x039a, 0x039b, 0x039c, 0x039d, 0x039f, 0x03a1, 0x0398, 0x03a8, 0x03a3, 0x03a4, 0x0389, 0x03a6, 0x03a9, 0x03a7, 0x03a5, 0x0396, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039}, // greek - {0x0410, 0x0412, 0x0421, 0x0414, 0x0415, 0x0416, 0x0413, 0x041d, 0x0049, 0x041f, 0x041a, 0x041b, 0x041c, 0x0417, 0x041e, 0x0420, 0x0424, 0x042f, 0x0426, 0x0422, 0x042d, 0x0427, 0x0428, 0x0425, 0x0423, 0x0411, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039}, // cyrillic - {0x05d0, 0x05d1, 0x05d2, 0x05d3, 0x05e3, 0x05d4, 0x05d6, 0x05d7, 0x05d5, 0x05d8, 0x05d9, 0x05da, 0x05db, 0x05dc, 0x05e1, 0x05dd, 0x05de, 0x05e0, 0x05e2, 0x05e4, 0x05e5, 0x05e6, 0x05e7, 0x05e8, 0x05e9, 0x05ea, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039}, // hebrew - {0x0905, 0x0915, 0x0917, 0x0918, 0x090f, 0x091a, 0x091c, 0x091f, 0x0049, 0x0920, 0x0923, 0x0924, 0x0926, 0x0927, 0x004f, 0x0928, 0x092a, 0x092d, 0x092e, 0x0930, 0x092b, 0x0932, 0x0935, 0x0938, 0x0939, 0x092c, 0x0966, 0x0967, 0x0968, 0x0969, 0x096a, 0x096b, 0x096c, 0x096d, 0x096e, 0x096f}, // Devanagari - {0x0d12, 0x0d15, 0x0d16, 0x0d17, 0x0d0b, 0x0d1a, 0x0d1c, 0x0d1f, 0x0049, 0x0d21, 0x0d24, 0x0d25, 0x0d26, 0x0d27, 0x0d20, 0x0d28, 0x0d2e, 0x0d30, 0x0d31, 0x0d32, 0x0d09, 0x0d34, 0x0d35, 0x0d36, 0x0d38, 0x0d39, 0x0d66, 0x0d67, 0x0d68, 0x0d69, 0x0d6a, 0x0d6b, 0x0d6c, 0x0d6d, 0x0d6e, 0x0d6f}, // Malayalam - {0x10a0, 0x10a1, 0x10a3, 0x10a6, 0x10a4, 0x10a9, 0x10ab, 0x10ac, 0x0049, 0x10ae, 0x10b0, 0x10b1, 0x10b2, 0x10b4, 0x10ad, 0x10b5, 0x10b6, 0x10b7, 0x10b8, 0x10b9, 0x10a8, 0x10ba, 0x10bb, 0x10bd, 0x10be, 0x10bf, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039}, // Georgian - {0x30a2, 0x30ab, 0x30ad, 0x30af, 0x30aa, 0x30b1, 0x30b3, 0x30b5, 0x0049, 0x30b9, 0x30c1, 0x30c8, 0x30ca, 0x30cc, 0x004f, 0x30d2, 0x30d5, 0x30d8, 0x30db, 0x30e1, 0x30a8, 0x30e2, 0x30e8, 0x30e9, 0x30ed, 0x30f2, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039}, // Katakana - {0x0e30, 0x0e01, 0x0e02, 0x0e04, 0x0e32, 0x0e07, 0x0e08, 0x0e09, 0x0049, 0x0e0a, 0x0e11, 0x0e14, 0x0e16, 0x0e17, 0x004f, 0x0e18, 0x0e1a, 0x0e1c, 0x0e21, 0x0e23, 0x0e2c, 0x0e25, 0x0e27, 0x0e2d, 0x0e2e, 0x0e2f, 0x0e50, 0x0e51, 0x0e52, 0x0e53, 0x0e54, 0x0e55, 0x0e56, 0x0e57, 0x0e58, 0x0e59}, // Thai - {0x0eb0, 0x0e81, 0x0e82, 0x0e84, 0x0ec3, 0x0e87, 0x0e88, 0x0e8a, 0x0ec4, 0x0e8d, 0x0e94, 0x0e97, 0x0e99, 0x0e9a, 0x004f, 0x0e9c, 0x0e9e, 0x0ea1, 0x0ea2, 0x0ea3, 0x0ebd, 0x0ea7, 0x0eaa, 0x0eab, 0x0ead, 0x0eaf, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039}, // Laos - {0x0556, 0x0532, 0x0533, 0x0534, 0x0535, 0x0538, 0x0539, 0x053a, 0x053b, 0x053d, 0x053f, 0x0540, 0x0541, 0x0543, 0x0555, 0x0547, 0x0548, 0x054a, 0x054d, 0x054e, 0x0545, 0x054f, 0x0550, 0x0551, 0x0552, 0x0553, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039}, // armenian - {0x099c, 0x0998, 0x0995, 0x0996, 0x09ae, 0x0997, 0x0999, 0x099a, 0x0049, 0x099d, 0x09a0, 0x09a1, 0x09a2, 0x09a3, 0x004f, 0x09a4, 0x09a5, 0x09a6, 0x09a8, 0x09aa, 0x099f, 0x09ac, 0x09ad, 0x09af, 0x09b2, 0x09b9, 0x09e6, 0x09e7, 0x09e8, 0x09e9, 0x09ea, 0x09eb, 0x09ec, 0x09ed, 0x09ee, 0x09ef}, // Bengali/Assamese - {0x0a05, 0x0a15, 0x0a17, 0x0a18, 0x0a0f, 0x0a1a, 0x0a1c, 0x0a1f, 0x0049, 0x0a20, 0x0a23, 0x0a24, 0x0a26, 0x0a27, 0x004f, 0x0a28, 0x0a2a, 0x0a2d, 0x0a2e, 0x0a30, 0x0a2b, 0x0a32, 0x0a35, 0x0a38, 0x0a39, 0x0a21, 0x0a66, 0x0a67, 0x0a68, 0x0a69, 0x0a6a, 0x0a6b, 0x0a6c, 0x0a6d, 0x0a6e, 0x0a6f}, // Gurmukhi - {0x0f58, 0x0f40, 0x0f41, 0x0f42, 0x0f64, 0x0f44, 0x0f45, 0x0f46, 0x0049, 0x0f47, 0x0f49, 0x0f55, 0x0f50, 0x0f4f, 0x004f, 0x0f51, 0x0f53, 0x0f54, 0x0f56, 0x0f5e, 0x0f60, 0x0f5f, 0x0f61, 0x0f62, 0x0f63, 0x0f66, 0x0f20, 0x0f21, 0x0f22, 0x0f23, 0x0f24, 0x0f25, 0x0f26, 0x0f27, 0x0f28, 0x0f29}, // Tibetan - {0x0628, 0x062a, 0x062d, 0x062e, 0x062B, 0x062f, 0x0630, 0x0631, 0x0627, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x0647, 0x0637, 0x0638, 0x0639, 0x063a, 0x0641, 0x0642, 0x062C, 0x0644, 0x0645, 0x0646, 0x0648, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039}, // Arabic - {0x1112, 0x1100, 0x1102, 0x1103, 0x1166, 0x1105, 0x1107, 0x1109, 0x1175, 0x1110, 0x1111, 0x1161, 0x1162, 0x1163, 0x110b, 0x1164, 0x1165, 0x1167, 0x1169, 0x1172, 0x1174, 0x110c, 0x110e, 0x110f, 0x116d, 0x116e, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039}, // Korean // 0xc601, 0xc77c, 0xc774, 0xc0bc, 0xc0ac, 0xc624, 0xc721, 0xce60, 0xd314, 0xad6c (vocal digits) - {0x1005, 0x1000, 0x1001, 0x1002, 0x1013, 0x1003, 0x1004, 0x101a, 0x0049, 0x1007, 0x100c, 0x100d, 0x100e, 0x1010, 0x101d, 0x1011, 0x1012, 0x101e, 0x1014, 0x1015, 0x1016, 0x101f, 0x1017, 0x1018, 0x100f, 0x101c, 0x1040, 0x1041, 0x1042, 0x1043, 0x1044, 0x1045, 0x1046, 0x1047, 0x1048, 0x1049}, // Burmese - {0x1789, 0x1780, 0x1781, 0x1782, 0x1785, 0x1783, 0x1784, 0x1787, 0x179a, 0x1788, 0x178a, 0x178c, 0x178d, 0x178e, 0x004f, 0x1791, 0x1792, 0x1793, 0x1794, 0x1795, 0x179f, 0x1796, 0x1798, 0x179b, 0x17a0, 0x17a2, 0x17e0, 0x17e1, 0x17e2, 0x17e3, 0x17e4, 0x17e5, 0x17e6, 0x17e7, 0x17e8, 0x17e9}, // Khmer - {0x0d85, 0x0d9a, 0x0d9c, 0x0d9f, 0x0d89, 0x0da2, 0x0da7, 0x0da9, 0x0049, 0x0dac, 0x0dad, 0x0daf, 0x0db1, 0x0db3, 0x004f, 0x0db4, 0x0db6, 0x0db8, 0x0db9, 0x0dba, 0x0d8b, 0x0dbb, 0x0dbd, 0x0dc0, 0x0dc3, 0x0dc4, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039}, // Sinhalese - {0x0794, 0x0780, 0x0781, 0x0782, 0x0797, 0x0783, 0x0784, 0x0785, 0x0049, 0x0786, 0x0787, 0x0788, 0x0789, 0x078a, 0x004f, 0x078b, 0x078c, 0x078d, 0x078e, 0x078f, 0x079c, 0x0790, 0x0791, 0x0792, 0x0793, 0x07b1, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039}, // Thaana - {0x3123, 0x3105, 0x3108, 0x3106, 0x3114, 0x3107, 0x3109, 0x310a, 0x0049, 0x310b, 0x310c, 0x310d, 0x310e, 0x310f, 0x004f, 0x3115, 0x3116, 0x3110, 0x3111, 0x3112, 0x3113, 0x3129, 0x3117, 0x3128, 0x3118, 0x3119, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039}, // Chinese - {0x2D49, 0x2D31, 0x2D33, 0x2D37, 0x2D53, 0x2D3C, 0x2D3D, 0x2D40, 0x2D4F, 0x2D43, 0x2D44, 0x2D45, 0x2D47, 0x2D4D, 0x2D54, 0x2D4E, 0x2D55, 0x2D56, 0x2D59, 0x2D5A, 0x2D62, 0x2D5B, 0x2D5C, 0x2D5F, 0x2D61, 0x2D63, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039}, // Tifinagh (BERBER) - {0x0b99, 0x0b95, 0x0b9a, 0x0b9f, 0x0b86, 0x0ba4, 0x0ba8, 0x0baa, 0x0049, 0x0bae, 0x0baf, 0x0bb0, 0x0bb2, 0x0bb5, 0x004f, 0x0bb4, 0x0bb3, 0x0bb1, 0x0b85, 0x0b88, 0x0b93, 0x0b89, 0x0b8e, 0x0b8f, 0x0b90, 0x0b92, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039}, // Tamil (digits 0xBE6-0xBEF) - {0x121B, 0x1260, 0x1264, 0x12F0, 0x121E, 0x134A, 0x1308, 0x1200, 0x0049, 0x12E8, 0x12AC, 0x1208, 0x1293, 0x1350, 0x12D0, 0x1354, 0x1240, 0x1244, 0x122C, 0x1220, 0x12C8, 0x1226, 0x1270, 0x1276, 0x1338, 0x12DC, 0x1372, 0x1369, 0x136a, 0x136b, 0x136c, 0x136d, 0x136e, 0x136f, 0x1370, 0x1371}, // Amharic (digits 1372|1369-1371) - {0x0C1E, 0x0C15, 0x0C17, 0x0C19, 0x0C2B, 0x0C1A, 0x0C1C, 0x0C1F, 0x0049, 0x0C20, 0x0C21, 0x0C23, 0x0C24, 0x0C25, 0x004f, 0x0C26, 0x0C27, 0x0C28, 0x0C2A, 0x0C2C, 0x0C2D, 0x0C2E, 0x0C30, 0x0C32, 0x0C33, 0x0C35, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039}, // Telugu - {0x0B1D, 0x0B15, 0x0B16, 0x0B17, 0x0B23, 0x0B18, 0x0B1A, 0x0B1C, 0x0049, 0x0B1F, 0x0B21, 0x0B22, 0x0B24, 0x0B25, 0x0B20, 0x0B26, 0x0B27, 0x0B28, 0x0B2A, 0x0B2C, 0x0B39, 0x0B2E, 0x0B2F, 0x0B30, 0x0B33, 0x0B38, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039}, // Odia - {0x0C92, 0x0C95, 0x0C96, 0x0C97, 0x0C8E, 0x0C99, 0x0C9A, 0x0C9B, 0x0049, 0x0C9C, 0x0CA0, 0x0CA1, 0x0CA3, 0x0CA4, 0x004f, 0x0CA6, 0x0CA7, 0x0CA8, 0x0CAA, 0x0CAB, 0x0C87, 0x0CAC, 0x0CAD, 0x0CB0, 0x0CB2, 0x0CB5, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039}, // Kannada - {0x0AB3, 0x0A97, 0x0A9C, 0x0AA1, 0x0A87, 0x0AA6, 0x0AAC, 0x0A95, 0x0049, 0x0A9A, 0x0A9F, 0x0AA4, 0x0AAA, 0x0AA0, 0x004f, 0x0AB0, 0x0AB5, 0x0A9E, 0x0AAE, 0x0AAB, 0x0A89, 0x0AB7, 0x0AA8, 0x0A9D, 0x0AA2, 0x0AAD, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039}, // Gujarati +static const UWORD ASCII_TO_UTF16[_ALPHABET_MAX][36] = { + // A-Z equivalents for ascii characters A to Z, 0-9 + // A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 + { + 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, + 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x0030, 0x0031, + 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039 + }, // roman + { + 0x0391, 0x0392, 0x039e, 0x0394, 0x0388, 0x0395, 0x0393, 0x0397, 0x0399, 0x03a0, 0x039a, 0x039b, 0x039c, 0x039d, + 0x039f, 0x03a1, 0x0398, 0x03a8, 0x03a3, 0x03a4, 0x0389, 0x03a6, 0x03a9, 0x03a7, 0x03a5, 0x0396, 0x0030, 0x0031, + 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039 + }, // greek + { + 0x0410, 0x0412, 0x0421, 0x0414, 0x0415, 0x0416, 0x0413, 0x041d, 0x0049, 0x041f, 0x041a, 0x041b, 0x041c, 0x0417, + 0x041e, 0x0420, 0x0424, 0x042f, 0x0426, 0x0422, 0x042d, 0x0427, 0x0428, 0x0425, 0x0423, 0x0411, 0x0030, 0x0031, + 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039 + }, // cyrillic + { + 0x05d0, 0x05d1, 0x05d2, 0x05d3, 0x05e3, 0x05d4, 0x05d6, 0x05d7, 0x05d5, 0x05d8, 0x05d9, 0x05da, 0x05db, 0x05dc, + 0x05e1, 0x05dd, 0x05de, 0x05e0, 0x05e2, 0x05e4, 0x05e5, 0x05e6, 0x05e7, 0x05e8, 0x05e9, 0x05ea, 0x0030, 0x0031, + 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039 + }, // hebrew + { + 0x0905, 0x0915, 0x0917, 0x0918, 0x090f, 0x091a, 0x091c, 0x091f, 0x0049, 0x0920, 0x0923, 0x0924, 0x0926, 0x0927, + 0x004f, 0x0928, 0x092a, 0x092d, 0x092e, 0x0930, 0x092b, 0x0932, 0x0935, 0x0938, 0x0939, 0x092c, 0x0966, 0x0967, + 0x0968, 0x0969, 0x096a, 0x096b, 0x096c, 0x096d, 0x096e, 0x096f + }, // Devanagari + { + 0x0d12, 0x0d15, 0x0d16, 0x0d17, 0x0d0b, 0x0d1a, 0x0d1c, 0x0d1f, 0x0049, 0x0d21, 0x0d24, 0x0d25, 0x0d26, 0x0d27, + 0x0d20, 0x0d28, 0x0d2e, 0x0d30, 0x0d31, 0x0d32, 0x0d09, 0x0d34, 0x0d35, 0x0d36, 0x0d38, 0x0d39, 0x0d66, 0x0d67, + 0x0d68, 0x0d69, 0x0d6a, 0x0d6b, 0x0d6c, 0x0d6d, 0x0d6e, 0x0d6f + }, // Malayalam + { + 0x10a0, 0x10a1, 0x10a3, 0x10a6, 0x10a4, 0x10a9, 0x10ab, 0x10ac, 0x0049, 0x10ae, 0x10b0, 0x10b1, 0x10b2, 0x10b4, + 0x10ad, 0x10b5, 0x10b6, 0x10b7, 0x10b8, 0x10b9, 0x10a8, 0x10ba, 0x10bb, 0x10bd, 0x10be, 0x10bf, 0x0030, 0x0031, + 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039 + }, // Georgian + { + 0x30a2, 0x30ab, 0x30ad, 0x30af, 0x30aa, 0x30b1, 0x30b3, 0x30b5, 0x0049, 0x30b9, 0x30c1, 0x30c8, 0x30ca, 0x30cc, + 0x004f, 0x30d2, 0x30d5, 0x30d8, 0x30db, 0x30e1, 0x30a8, 0x30e2, 0x30e8, 0x30e9, 0x30ed, 0x30f2, 0x0030, 0x0031, + 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039 + }, // Katakana + { + 0x0e30, 0x0e01, 0x0e02, 0x0e04, 0x0e32, 0x0e07, 0x0e08, 0x0e09, 0x0049, 0x0e0a, 0x0e11, 0x0e14, 0x0e16, 0x0e17, + 0x004f, 0x0e18, 0x0e1a, 0x0e1c, 0x0e21, 0x0e23, 0x0e2c, 0x0e25, 0x0e27, 0x0e2d, 0x0e2e, 0x0e2f, 0x0e50, 0x0e51, + 0x0e52, 0x0e53, 0x0e54, 0x0e55, 0x0e56, 0x0e57, 0x0e58, 0x0e59 + }, // Thai + { + 0x0eb0, 0x0e81, 0x0e82, 0x0e84, 0x0ec3, 0x0e87, 0x0e88, 0x0e8a, 0x0ec4, 0x0e8d, 0x0e94, 0x0e97, 0x0e99, 0x0e9a, + 0x004f, 0x0e9c, 0x0e9e, 0x0ea1, 0x0ea2, 0x0ea3, 0x0ebd, 0x0ea7, 0x0eaa, 0x0eab, 0x0ead, 0x0eaf, 0x0030, 0x0031, + 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039 + }, // Laos + { + 0x0556, 0x0532, 0x0533, 0x0534, 0x0535, 0x0538, 0x0539, 0x053a, 0x053b, 0x053d, 0x053f, 0x0540, 0x0541, 0x0543, + 0x0555, 0x0547, 0x0548, 0x054a, 0x054d, 0x054e, 0x0545, 0x054f, 0x0550, 0x0551, 0x0552, 0x0553, 0x0030, 0x0031, + 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039 + }, // armenian + { + 0x099c, 0x0998, 0x0995, 0x0996, 0x09ae, 0x0997, 0x0999, 0x099a, 0x0049, 0x099d, 0x09a0, 0x09a1, 0x09a2, 0x09a3, + 0x004f, 0x09a4, 0x09a5, 0x09a6, 0x09a8, 0x09aa, 0x099f, 0x09ac, 0x09ad, 0x09af, 0x09b2, 0x09b9, 0x09e6, 0x09e7, + 0x09e8, 0x09e9, 0x09ea, 0x09eb, 0x09ec, 0x09ed, 0x09ee, 0x09ef + }, // Bengali/Assamese + { + 0x0a05, 0x0a15, 0x0a17, 0x0a18, 0x0a0f, 0x0a1a, 0x0a1c, 0x0a1f, 0x0049, 0x0a20, 0x0a23, 0x0a24, 0x0a26, 0x0a27, + 0x004f, 0x0a28, 0x0a2a, 0x0a2d, 0x0a2e, 0x0a30, 0x0a2b, 0x0a32, 0x0a35, 0x0a38, 0x0a39, 0x0a21, 0x0a66, 0x0a67, + 0x0a68, 0x0a69, 0x0a6a, 0x0a6b, 0x0a6c, 0x0a6d, 0x0a6e, 0x0a6f + }, // Gurmukhi + { + 0x0f58, 0x0f40, 0x0f41, 0x0f42, 0x0f64, 0x0f44, 0x0f45, 0x0f46, 0x0049, 0x0f47, 0x0f49, 0x0f55, 0x0f50, 0x0f4f, + 0x004f, 0x0f51, 0x0f53, 0x0f54, 0x0f56, 0x0f5e, 0x0f60, 0x0f5f, 0x0f61, 0x0f62, 0x0f63, 0x0f66, 0x0f20, 0x0f21, + 0x0f22, 0x0f23, 0x0f24, 0x0f25, 0x0f26, 0x0f27, 0x0f28, 0x0f29 + }, // Tibetan + { + 0x0628, 0x062a, 0x062d, 0x062e, 0x062B, 0x062f, 0x0630, 0x0631, 0x0627, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, + 0x0647, 0x0637, 0x0638, 0x0639, 0x063a, 0x0641, 0x0642, 0x062C, 0x0644, 0x0645, 0x0646, 0x0648, 0x0030, 0x0031, + 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039 + }, // Arabic + { + 0x1112, 0x1100, 0x1102, 0x1103, 0x1166, 0x1105, 0x1107, 0x1109, 0x1175, 0x1110, 0x1111, 0x1161, 0x1162, 0x1163, + 0x110b, 0x1164, 0x1165, 0x1167, 0x1169, 0x1172, 0x1174, 0x110c, 0x110e, 0x110f, 0x116d, 0x116e, 0x0030, 0x0031, + 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039 + }, // Korean // 0xc601, 0xc77c, 0xc774, 0xc0bc, 0xc0ac, 0xc624, 0xc721, 0xce60, 0xd314, 0xad6c (vocal digits) + { + 0x1005, 0x1000, 0x1001, 0x1002, 0x1013, 0x1003, 0x1004, 0x101a, 0x0049, 0x1007, 0x100c, 0x100d, 0x100e, 0x1010, + 0x101d, 0x1011, 0x1012, 0x101e, 0x1014, 0x1015, 0x1016, 0x101f, 0x1017, 0x1018, 0x100f, 0x101c, 0x1040, 0x1041, + 0x1042, 0x1043, 0x1044, 0x1045, 0x1046, 0x1047, 0x1048, 0x1049 + }, // Burmese + { + 0x1789, 0x1780, 0x1781, 0x1782, 0x1785, 0x1783, 0x1784, 0x1787, 0x179a, 0x1788, 0x178a, 0x178c, 0x178d, 0x178e, + 0x004f, 0x1791, 0x1792, 0x1793, 0x1794, 0x1795, 0x179f, 0x1796, 0x1798, 0x179b, 0x17a0, 0x17a2, 0x17e0, 0x17e1, + 0x17e2, 0x17e3, 0x17e4, 0x17e5, 0x17e6, 0x17e7, 0x17e8, 0x17e9 + }, // Khmer + { + 0x0d85, 0x0d9a, 0x0d9c, 0x0d9f, 0x0d89, 0x0da2, 0x0da7, 0x0da9, 0x0049, 0x0dac, 0x0dad, 0x0daf, 0x0db1, 0x0db3, + 0x004f, 0x0db4, 0x0db6, 0x0db8, 0x0db9, 0x0dba, 0x0d8b, 0x0dbb, 0x0dbd, 0x0dc0, 0x0dc3, 0x0dc4, 0x0030, 0x0031, + 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039 + }, // Sinhalese + { + 0x0794, 0x0780, 0x0781, 0x0782, 0x0797, 0x0783, 0x0784, 0x0785, 0x0049, 0x0786, 0x0787, 0x0788, 0x0789, 0x078a, + 0x004f, 0x078b, 0x078c, 0x078d, 0x078e, 0x078f, 0x079c, 0x0790, 0x0791, 0x0792, 0x0793, 0x07b1, 0x0030, 0x0031, + 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039 + }, // Thaana + { + 0x3123, 0x3105, 0x3108, 0x3106, 0x3114, 0x3107, 0x3109, 0x310a, 0x0049, 0x310b, 0x310c, 0x310d, 0x310e, 0x310f, + 0x004f, 0x3115, 0x3116, 0x3110, 0x3111, 0x3112, 0x3113, 0x3129, 0x3117, 0x3128, 0x3118, 0x3119, 0x0030, 0x0031, + 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039 + }, // Chinese + { + 0x2D49, 0x2D31, 0x2D33, 0x2D37, 0x2D53, 0x2D3C, 0x2D3D, 0x2D40, 0x2D4F, 0x2D43, 0x2D44, 0x2D45, 0x2D47, 0x2D4D, + 0x2D54, 0x2D4E, 0x2D55, 0x2D56, 0x2D59, 0x2D5A, 0x2D62, 0x2D5B, 0x2D5C, 0x2D5F, 0x2D61, 0x2D63, 0x0030, 0x0031, + 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039 + }, // Tifinagh (BERBER) + { + 0x0b99, 0x0b95, 0x0b9a, 0x0b9f, 0x0b86, 0x0ba4, 0x0ba8, 0x0baa, 0x0049, 0x0bae, 0x0baf, 0x0bb0, 0x0bb2, 0x0bb5, + 0x004f, 0x0bb4, 0x0bb3, 0x0bb1, 0x0b85, 0x0b88, 0x0b93, 0x0b89, 0x0b8e, 0x0b8f, 0x0b90, 0x0b92, 0x0030, 0x0031, + 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039 + }, // Tamil (digits 0xBE6-0xBEF) + { + 0x121B, 0x1260, 0x1264, 0x12F0, 0x121E, 0x134A, 0x1308, 0x1200, 0x0049, 0x12E8, 0x12AC, 0x1208, 0x1293, 0x1350, + 0x12D0, 0x1354, 0x1240, 0x1244, 0x122C, 0x1220, 0x12C8, 0x1226, 0x1270, 0x1276, 0x1338, 0x12DC, 0x1372, 0x1369, + 0x136a, 0x136b, 0x136c, 0x136d, 0x136e, 0x136f, 0x1370, 0x1371 + }, // Amharic (digits 1372|1369-1371) + { + 0x0C1E, 0x0C15, 0x0C17, 0x0C19, 0x0C2B, 0x0C1A, 0x0C1C, 0x0C1F, 0x0049, 0x0C20, 0x0C21, 0x0C23, 0x0C24, 0x0C25, + 0x004f, 0x0C26, 0x0C27, 0x0C28, 0x0C2A, 0x0C2C, 0x0C2D, 0x0C2E, 0x0C30, 0x0C32, 0x0C33, 0x0C35, 0x0030, 0x0031, + 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039 + }, // Telugu + { + 0x0B1D, 0x0B15, 0x0B16, 0x0B17, 0x0B23, 0x0B18, 0x0B1A, 0x0B1C, 0x0049, 0x0B1F, 0x0B21, 0x0B22, 0x0B24, 0x0B25, + 0x0B20, 0x0B26, 0x0B27, 0x0B28, 0x0B2A, 0x0B2C, 0x0B39, 0x0B2E, 0x0B2F, 0x0B30, 0x0B33, 0x0B38, 0x0030, 0x0031, + 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039 + }, // Odia + { + 0x0C92, 0x0C95, 0x0C96, 0x0C97, 0x0C8E, 0x0C99, 0x0C9A, 0x0C9B, 0x0049, 0x0C9C, 0x0CA0, 0x0CA1, 0x0CA3, 0x0CA4, + 0x004f, 0x0CA6, 0x0CA7, 0x0CA8, 0x0CAA, 0x0CAB, 0x0C87, 0x0CAC, 0x0CAD, 0x0CB0, 0x0CB2, 0x0CB5, 0x0030, 0x0031, + 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039 + }, // Kannada + { + 0x0AB3, 0x0A97, 0x0A9C, 0x0AA1, 0x0A87, 0x0AA6, 0x0AAC, 0x0A95, 0x0049, 0x0A9A, 0x0A9F, 0x0AA4, 0x0AAA, 0x0AA0, + 0x004f, 0x0AB0, 0x0AB5, 0x0A9E, 0x0AAE, 0x0AAB, 0x0A89, 0x0AB7, 0x0AA8, 0x0A9D, 0x0AA2, 0x0AAD, 0x0030, 0x0031, + 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039 + }, // Gujarati }; /////////////////////////////////////////////////////////////////////////////////////////////// @@ -2499,17 +2762,16 @@ static const UWORD ASCII_TO_UTF16[_ALPHABET_MAX][36] = { // A-Z equivalents for /////////////////////////////////////////////////////////////////////////////////////////////// - /// PRIVATE convert a mapcode to an ABJAD-format (never more than 2 non-digits in a row) -static char *convertToAbjad(char *targetAsciiString, const char *sourceAsciiString, int maxLength) { +static char* convertToAbjad(char* targetAsciiString, const char* sourceAsciiString, int maxLength) { int form, i, dot, inarow, len; - const char *rest; + const char* rest; ASSERT(targetAsciiString); ASSERT(sourceAsciiString); - len = (int) strlen(sourceAsciiString); + len = (int)strlen(sourceAsciiString); rest = strchr(sourceAsciiString, '-'); if (rest != NULL) { - len = ((int) (rest - sourceAsciiString)); + len = ((int)(rest - sourceAsciiString)); } if (len >= maxLength) { len = maxLength - 1; @@ -2521,8 +2783,8 @@ static char *convertToAbjad(char *targetAsciiString, const char *sourceAsciiStri lengthCopy(targetAsciiString, sourceAsciiString, len, maxLength); unpackIfAllDigits(targetAsciiString); - len = (int) strlen(targetAsciiString); - dot = (int) (strchr(targetAsciiString, '.') - targetAsciiString); + len = (int)strlen(targetAsciiString); + dot = (int)(strchr(targetAsciiString, '.') - targetAsciiString); form = dot * 10 + (len - dot - 1); @@ -2534,17 +2796,19 @@ static char *convertToAbjad(char *targetAsciiString, const char *sourceAsciiStri inarow++; if (decodeChar(c) <= 9) { inarow = 0; - } else if (inarow > 2) { + } + else if (inarow > 2) { break; } } } if (dot < 2 || dot > 5 || (inarow < 3 && - (form == 22 || form == 32 || form == 33 || form == 42 || form == 43 || form == 44 || - form == 54))) { + (form == 22 || form == 32 || form == 33 || form == 42 || form == 43 || form == 44 || + form == 54))) { // no need to do anything, return input unchanged return safeCopy(targetAsciiString, sourceAsciiString, maxLength); - } else if (form >= 22 && form <= 54) { + } + else if (form >= 22 && form <= 54) { char c1, c2, c3 = '?'; int c = decodeChar(targetAsciiString[2]); if (c < 0) { @@ -2556,17 +2820,21 @@ static char *convertToAbjad(char *targetAsciiString, const char *sourceAsciiStri c1 = ENCODE_CHARS[c / 100]; c2 = ENCODE_CHARS[(c % 100) / 10]; c3 = ENCODE_CHARS[c % 10]; - } else if (len == 7) { + } + else if (len == 7) { if (form == 24) { c += 7; - } else if (form == 33) { + } + else if (form == 33) { c += 38; - } else if (form == 42) { + } + else if (form == 42) { c += 69; } c1 = ENCODE_CHARS[c / 10]; c2 = ENCODE_CHARS[c % 10]; - } else { + } + else { c1 = ENCODE_CHARS[2 + (c / 8)]; c2 = ENCODE_CHARS[2 + (c % 8)]; } @@ -2577,28 +2845,33 @@ static char *convertToAbjad(char *targetAsciiString, const char *sourceAsciiStri targetAsciiString[5] = targetAsciiString[4]; targetAsciiString[4] = c2; targetAsciiString[3] = c1; -// targetAsciiString[2] = '.'; -// targetAsciiString[1] = targetAsciiString[1]; -// targetAsciiString[0] = targetAsciiString[0]; - } else if (form == 23) { // s0 s1 . s3 s4 s5 -> s0 s1 . C1 C2 s4 s5 + // targetAsciiString[2] = '.'; + // targetAsciiString[1] = targetAsciiString[1]; + // targetAsciiString[0] = targetAsciiString[0]; + } + else if (form == 23) { + // s0 s1 . s3 s4 s5 -> s0 s1 . C1 C2 s4 s5 targetAsciiString[7] = 0; targetAsciiString[6] = targetAsciiString[5]; targetAsciiString[5] = targetAsciiString[4]; targetAsciiString[4] = c2; targetAsciiString[3] = c1; -// targetAsciiString[2] = '.'; -// targetAsciiString[1] = targetAsciiString[1]; -// targetAsciiString[0] = targetAsciiString[0]; - } else if (form == 32) { // s0 s1 s2 . s4 s5 -> s0 s1 . C* C2 s4 s5 + // targetAsciiString[2] = '.'; + // targetAsciiString[1] = targetAsciiString[1]; + // targetAsciiString[0] = targetAsciiString[0]; + } + else if (form == 32) { + // s0 s1 s2 . s4 s5 -> s0 s1 . C* C2 s4 s5 targetAsciiString[7] = 0; targetAsciiString[6] = targetAsciiString[5]; targetAsciiString[5] = targetAsciiString[4]; targetAsciiString[4] = c2; - targetAsciiString[3] = (char) (c1 + 4); + targetAsciiString[3] = (char)(c1 + 4); targetAsciiString[2] = '.'; -// targetAsciiString[1] = targetAsciiString[1]; -// targetAsciiString[0] = targetAsciiString[0]; - } else if (form == 24 || form == 33 || form == 42) { + // targetAsciiString[1] = targetAsciiString[1]; + // targetAsciiString[0] = targetAsciiString[0]; + } + else if (form == 24 || form == 33 || form == 42) { // s0 s1 . s3 s4 s5 s6 -> s0 s1 C1 . s4 C2 s5 s6 // s0 s1 s2 . s4 s5 s6 -> s0 s1 C1 . s4 C2 s5 s6 // s0 s1 s2 s3 . s5 s6 -> s0 s1 C1 . s3 C2 s5 s6 @@ -2609,67 +2882,73 @@ static char *convertToAbjad(char *targetAsciiString, const char *sourceAsciiStri targetAsciiString[4] = targetAsciiString[(form == 42 ? 3 : 4)]; targetAsciiString[3] = '.'; targetAsciiString[2] = c1; -// targetAsciiString[1] = targetAsciiString[1]; -// targetAsciiString[0] = targetAsciiString[0]; - } else if (form == 34) { // s0 s1 s2 . s4 s5 s6 s7 -> s0 s1 C1 . s4 s5 C2 S6 S7 + // targetAsciiString[1] = targetAsciiString[1]; + // targetAsciiString[0] = targetAsciiString[0]; + } + else if (form == 34) { + // s0 s1 s2 . s4 s5 s6 s7 -> s0 s1 C1 . s4 s5 C2 S6 S7 targetAsciiString[9] = 0; targetAsciiString[8] = targetAsciiString[7]; targetAsciiString[7] = targetAsciiString[6]; targetAsciiString[6] = c2; -// targetAsciiString[5] = targetAsciiString[5]; -// targetAsciiString[4] = targetAsciiString[4]; -// targetAsciiString[3] = '.'; + // targetAsciiString[5] = targetAsciiString[5]; + // targetAsciiString[4] = targetAsciiString[4]; + // targetAsciiString[3] = '.'; targetAsciiString[2] = c1; -// targetAsciiString[1] = targetAsciiString[1]; -// targetAsciiString[0] = targetAsciiString[0]; - } else if (form == 43) { // s0 s1 s2 s3 . s5 s6 s7 -> s0 s1 C* . s3 s5 C2 S6 S7 + // targetAsciiString[1] = targetAsciiString[1]; + // targetAsciiString[0] = targetAsciiString[0]; + } + else if (form == 43) { + // s0 s1 s2 s3 . s5 s6 s7 -> s0 s1 C* . s3 s5 C2 S6 S7 targetAsciiString[9] = 0; targetAsciiString[8] = targetAsciiString[7]; targetAsciiString[7] = targetAsciiString[6]; targetAsciiString[6] = c2; -// targetAsciiString[5] = targetAsciiString[5]; + // targetAsciiString[5] = targetAsciiString[5]; targetAsciiString[4] = targetAsciiString[3]; targetAsciiString[3] = '.'; - targetAsciiString[2] = (char) (c1 + 4); -// targetAsciiString[1] = targetAsciiString[1]; -// targetAsciiString[0] = targetAsciiString[0]; - } else if (form == 44) { + targetAsciiString[2] = (char)(c1 + 4); + // targetAsciiString[1] = targetAsciiString[1]; + // targetAsciiString[0] = targetAsciiString[0]; + } + else if (form == 44) { targetAsciiString[10] = 0; targetAsciiString[9] = targetAsciiString[7]; targetAsciiString[8] = c3; targetAsciiString[7] = targetAsciiString[6]; targetAsciiString[6] = targetAsciiString[5]; targetAsciiString[5] = c2; -// targetAsciiString[4] = '.'; -// targetAsciiString[3] = targetAsciiString[3]; + // targetAsciiString[4] = '.'; + // targetAsciiString[3] = targetAsciiString[3]; targetAsciiString[2] = c1; -// targetAsciiString[1] = targetAsciiString[1]; -// targetAsciiString[0] = targetAsciiString[0]; - } else if (form == 54) { + // targetAsciiString[1] = targetAsciiString[1]; + // targetAsciiString[0] = targetAsciiString[0]; + } + else if (form == 54) { targetAsciiString[11] = 0; targetAsciiString[10] = targetAsciiString[8]; targetAsciiString[9] = c3; targetAsciiString[8] = targetAsciiString[7]; targetAsciiString[7] = targetAsciiString[6]; targetAsciiString[6] = c2; -// targetAsciiString[5] = '.'; -// targetAsciiString[4] = targetAsciiString[4]; -// targetAsciiString[3] = targetAsciiString[3]; + // targetAsciiString[5] = '.'; + // targetAsciiString[4] = targetAsciiString[4]; + // targetAsciiString[3] = targetAsciiString[3]; targetAsciiString[2] = c1; -// targetAsciiString[1] = targetAsciiString[1]; -// targetAsciiString[0] = targetAsciiString[0]; + // targetAsciiString[1] = targetAsciiString[1]; + // targetAsciiString[0] = targetAsciiString[0]; } } repackIfAllDigits(targetAsciiString, 0); if (rest) { - int totalLen = (int) strlen(targetAsciiString); - int needed = (int) strlen(rest); + int totalLen = (int)strlen(targetAsciiString); + int needed = (int)strlen(rest); int tocopy = maxLength - totalLen - 1; if (tocopy > needed) { tocopy = needed; } if (tocopy > 0) { - memcpy(targetAsciiString + totalLen, rest, (size_t) tocopy); + memcpy(targetAsciiString + totalLen, rest, (size_t)tocopy); targetAsciiString[totalLen + tocopy] = 0; } } @@ -2677,12 +2956,12 @@ static char *convertToAbjad(char *targetAsciiString, const char *sourceAsciiStri } -static UWORD *encodeUtf16(UWORD *utf16String, const int maxLength, const char *asciiString, +static UWORD* encodeUtf16(UWORD* utf16String, const int maxLength, const char* asciiString, const enum Alphabet alphabet) // convert mapcode string alphabet { - UWORD *w = utf16String; - const UWORD *e = w + maxLength - 1; - const char *r = asciiString; + UWORD* w = utf16String; + const UWORD* e = w + maxLength - 1; + const char* r = asciiString; ASSERT(utf16String); ASSERT(asciiString); while (*r != 0 && w < e) { @@ -2690,13 +2969,19 @@ static UWORD *encodeUtf16(UWORD *utf16String, const int maxLength, const char *a if ((c >= 'a') && (c <= 'z')) { c += ('A' - 'a'); } - if ((c < ' ') || (c > 'Z')) { // not in any valid range? - *w++ = (UWORD) c; // leave untranslated - } else if ((c >= '0') && (c <= '9')) { // digit? - *w++ = ASCII_TO_UTF16[alphabet][26 + (int) c - '0']; - } else if (c < 'A') { // valid but not a letter (e.g. a dot, a space...) - *w++ = (UWORD) c; // leave untranslated - } else { + if ((c < ' ') || (c > 'Z')) { + // not in any valid range? + *w++ = (UWORD)c; // leave untranslated + } + else if ((c >= '0') && (c <= '9')) { + // digit? + *w++ = ASCII_TO_UTF16[alphabet][26 + (int)c - '0']; + } + else if (c < 'A') { + // valid but not a letter (e.g. a dot, a space...) + *w++ = (UWORD)c; // leave untranslated + } + else { *w++ = ASCII_TO_UTF16[alphabet][c - 'A']; } } @@ -2706,11 +2991,11 @@ static UWORD *encodeUtf16(UWORD *utf16String, const int maxLength, const char *a // PUBLIC - convert as much as will fit of mapcode into utf16String -UWORD *convertToAlphabet(UWORD *utf16String, int maxLength, const char *asciiString, +UWORD* convertToAlphabet(UWORD* utf16String, int maxLength, const char* asciiString, enum Alphabet alphabet) // 0=roman, 2=cyrillic { - UWORD *startbuf = utf16String; - UWORD *lastspot = &utf16String[maxLength - 1]; + UWORD* startbuf = utf16String; + UWORD* lastspot = &utf16String[maxLength - 1]; ASSERT(utf16String); ASSERT(asciiString); if (maxLength > 0) { @@ -2724,15 +3009,16 @@ UWORD *convertToAlphabet(UWORD *utf16String, int maxLength, const char *asciiStr // straight-copy everything up to and including first space { - const char *e = strchr(asciiString, ' '); + const char* e = strchr(asciiString, ' '); if (e) { while (asciiString <= e) { - if (utf16String == lastspot) { // buffer fully filled? + if (utf16String == lastspot) { + // buffer fully filled? // zero-terminate and return *utf16String = 0; return startbuf; } - *utf16String++ = (UWORD) *asciiString++; + *utf16String++ = (UWORD)*asciiString++; } while (*asciiString == ' ') { asciiString++; @@ -2746,11 +3032,12 @@ UWORD *convertToAlphabet(UWORD *utf16String, int maxLength, const char *asciiStr } // re-pack E/U-voweled mapcodes when necessary: - if (alphabet == ALPHABET_GREEK) { // alphabet has fewer characters than Roman! + if (alphabet == ALPHABET_GREEK) { + // alphabet has fewer characters than Roman! if (strchr(asciiString, 'E') || strchr(asciiString, 'U') || strchr(asciiString, 'e') || strchr(asciiString, 'u')) { // copy trimmed mapcode into temporary buffer targetAsciiString - int len = (int) strlen(asciiString); + int len = (int)strlen(asciiString); if (len < MAX_MAPCODE_RESULT_ASCII_LEN) { while (len > 0 && asciiString[len - 1] > 0 && asciiString[len - 1] <= 32) { len--; @@ -2764,7 +3051,7 @@ UWORD *convertToAlphabet(UWORD *utf16String, int maxLength, const char *asciiStr } } - encodeUtf16(utf16String, 1 + (int) (lastspot - utf16String), asciiString, alphabet); + encodeUtf16(utf16String, 1 + (int)(lastspot - utf16String), asciiString, alphabet); } return startbuf; } @@ -2773,20 +3060,22 @@ UWORD *convertToAlphabet(UWORD *utf16String, int maxLength, const char *asciiStr /** * Convert a zero-terminated UTF16 to a UTF8 string */ -char *convertUtf16ToUtf8(char *utf8, const UWORD *utf16) { +char* convertUtf16ToUtf8(char* utf8, const UWORD* utf16) { ASSERT(utf16); ASSERT(utf8); while (*utf16) { UWORD c = *utf16++; if (c < 0x80) { - *utf8++ = (char) c; - } else if (c < 0x800) { - *utf8++ = (char) (0xC0 + (c >> 6)); - *utf8++ = (char) (0x80 + (c & 63)); - } else { - *utf8++ = (char) (0xE0 + (c >> 12)); - *utf8++ = (char) (0x80 + ((c >> 6) & 63)); - *utf8++ = (char) (0x80 + (c & 63)); + *utf8++ = (char)c; + } + else if (c < 0x800) { + *utf8++ = (char)(0xC0 + (c >> 6)); + *utf8++ = (char)(0x80 + (c & 63)); + } + else { + *utf8++ = (char)(0xE0 + (c >> 12)); + *utf8++ = (char)(0x80 + ((c >> 6) & 63)); + *utf8++ = (char)(0x80 + (c & 63)); } } *utf8 = 0; @@ -2794,7 +3083,7 @@ char *convertUtf16ToUtf8(char *utf8, const UWORD *utf16) { } // Caller must make sure utf8String can hold at least MAX_MAPCODE_RESULT_LEN characters (including 0-terminator). -UWORD *convertMapcodeToAlphabetUtf16(UWORD *utf16String, const char *mapcodeString, enum Alphabet alphabet) { +UWORD* convertMapcodeToAlphabetUtf16(UWORD* utf16String, const char* mapcodeString, enum Alphabet alphabet) { ASSERT(utf16String); ASSERT(mapcodeString); ASSERT(alphabet > _ALPHABET_MIN && alphabet < _ALPHABET_MAX); @@ -2806,7 +3095,7 @@ UWORD *convertMapcodeToAlphabetUtf16(UWORD *utf16String, const char *mapcodeStri } -char *convertMapcodeToAlphabetUtf8(char *utf8String, const char *mapcodeString, enum Alphabet alphabet) { +char* convertMapcodeToAlphabetUtf8(char* utf8String, const char* mapcodeString, enum Alphabet alphabet) { UWORD utf16[MAX_MAPCODE_RESULT_UTF16_LEN + 1]; return convertUtf16ToUtf8(utf8String, convertMapcodeToAlphabetUtf16(utf16, mapcodeString, alphabet)); } @@ -2818,20 +3107,22 @@ char *convertMapcodeToAlphabetUtf8(char *utf8String, const char *mapcodeString, // /////////////////////////////////////////////////////////////////////////////////////////////// -// PUBLIC - returns name of territory in (sufficiently large!) result string. +// PUBLIC - returns name of territory in (sufficiently large!) result string. // useShortNames: 0=full 1=short // returns empty string in case of error -char *getTerritoryIsoName(char *territoryISO, enum Territory territory, int useShortName) { +char* getTerritoryIsoName(char* territoryISO, enum Territory territory, int useShortName) { ASSERT(territoryISO); ASSERT(useShortName == 0 || useShortName == 1); if (territory <= _TERRITORY_MIN || territory >= _TERRITORY_MAX) { *territoryISO = 0; - } else { - const char *alphaCode = ISO3166_ALPHA[INDEX_OF_TERRITORY(territory)]; - const char *hyphen = strchr(alphaCode, '-'); + } + else { + const char* alphaCode = ISO3166_ALPHA[INDEX_OF_TERRITORY(territory)]; + const char* hyphen = strchr(alphaCode, '-'); if (useShortName && hyphen != NULL) { strcpy(territoryISO, hyphen + 1); - } else { + } + else { strcpy(territoryISO, alphaCode); } } @@ -2859,7 +3150,8 @@ enum Territory getCountryOrParentCountry(enum Territory territory) { // PUBLIC - returns nonzero if coordinate is near more than one territory border int multipleBordersNearby(double latDeg, double lonDeg, enum Territory territory) { const enum Territory ccode = territory; - if ((ccode > _TERRITORY_MIN) && (ccode != TERRITORY_AAA)) { // valid territory, not earth + if ((ccode > _TERRITORY_MIN) && (ccode != TERRITORY_AAA)) { + // valid territory, not earth const enum Territory parentTerritoryCode = getParentCountryOf(territory); if (parentTerritoryCode != TERRITORY_NONE) { // there is a parent! check its borders as well... @@ -2890,18 +3182,18 @@ int multipleBordersNearby(double latDeg, double lonDeg, enum Territory territory } -static int compareAlphaCode(const void *e1, const void *e2) { - const AlphaRec *a1 = (const AlphaRec *) e1; - const AlphaRec *a2 = (const AlphaRec *) e2; +static int compareAlphaCode(const void* e1, const void* e2) { + const AlphaRec* a1 = (const AlphaRec*)e1; + const AlphaRec* a2 = (const AlphaRec*)e2; ASSERT(e1); ASSERT(e2); return strcmp(a1->alphaCode, a2->alphaCode); } // cmp -static enum Territory findMatch(const int parentNumber, const char *territoryISO) { +static enum Territory findMatch(const int parentNumber, const char* territoryISO) { // build an uppercase search term char codeISO[MAX_ISOCODE_ASCII_LEN + 1]; - const char *r = territoryISO; + const char* r = territoryISO; int len = 0; ASSERT(territoryISO); @@ -2922,14 +3214,16 @@ static enum Territory findMatch(const int parentNumber, const char *territoryISO } codeISO[len] = 0; makeUppercase(codeISO); - { // binary-search the result - const AlphaRec *p; + { + // binary-search the result + const AlphaRec* p; AlphaRec t; t.alphaCode = codeISO; - p = (const AlphaRec *) bsearch(&t, ALPHA_SEARCH, NR_TERRITORY_RECS, sizeof(AlphaRec), compareAlphaCode); + p = (const AlphaRec*)bsearch(&t, ALPHA_SEARCH, NR_TERRITORY_RECS, sizeof(AlphaRec), compareAlphaCode); if (p) { - if (strcmp(t.alphaCode, p->alphaCode) == 0) { // only interested in PERFECT match + if (strcmp(t.alphaCode, p->alphaCode) == 0) { + // only interested in PERFECT match return p->territory; } // match } // found @@ -2940,7 +3234,7 @@ static enum Territory findMatch(const int parentNumber, const char *territoryISO // PUBLIC - returns territory of territoryISO (or negative if not found). // optionalTerritoryContext: pass to handle ambiguities (pass TERRITORY_NONE if unknown). -enum Territory getTerritoryCode(const char *territoryISO, enum Territory optionalTerritoryContext) { +enum Territory getTerritoryCode(const char* territoryISO, enum Territory optionalTerritoryContext) { if (territoryISO == NULL) { return TERRITORY_NONE; } @@ -2952,9 +3246,11 @@ enum Territory getTerritoryCode(const char *territoryISO, enum Territory optiona if (territoryISO[0] && territoryISO[1]) { if (territoryISO[2] == '-') { return findMatch(getParentNumber(territoryISO, 2), territoryISO + 3); - } else if (territoryISO[2] && territoryISO[3] == '-') { + } + else if (territoryISO[2] && territoryISO[3] == '-') { return findMatch(getParentNumber(territoryISO, 3), territoryISO + 4); - } else { + } + else { enum Territory b; int parentNumber = 0; if (optionalTerritoryContext > _TERRITORY_MIN) { @@ -2972,23 +3268,24 @@ enum Territory getTerritoryCode(const char *territoryISO, enum Territory optiona // PUBLIC - decode string into lat,lon; returns negative in case of error -enum MapcodeError decodeMapcodeToLatLonUtf8(double *latDeg, double *lonDeg, - const char *mapcode, enum Territory territory, - MapcodeElements *mapcodeElements) { +enum MapcodeError decodeMapcodeToLatLonUtf8(double* latDeg, double* lonDeg, + const char* mapcode, enum Territory territory, + MapcodeElements* mapcodeElements) { if ((latDeg == NULL) || (lonDeg == NULL) || (mapcode == NULL)) { return ERR_BAD_ARGUMENTS; - } else { + } + else { enum MapcodeError ret; DecodeRec dec = { - {"", TERRITORY_NONE, "", 0, ""}, - 0, - 0, - 0, - TERRITORY_NONE, - 0, - {0.0, 0.0}, - {0, 0}, - {0.0, 0.0, 0.0, 0.0} + {"", TERRITORY_NONE, "", 0, ""}, + 0, + 0, + 0, + TERRITORY_NONE, + 0, + {0.0, 0.0}, + {0, 0}, + {0.0, 0.0, 0.0, 0.0} }; dec.orginput = mapcode; dec.context = territory; @@ -3006,25 +3303,26 @@ enum MapcodeError decodeMapcodeToLatLonUtf8(double *latDeg, double *lonDeg, // PUBLIC - decode string into lat,lon; returns negative in case of error -enum MapcodeError decodeMapcodeToLatLonUtf16(double *latDeg, double *lonDeg, - const UWORD *mapcode, enum Territory territory, - MapcodeElements *mapcodeElements) { +enum MapcodeError decodeMapcodeToLatLonUtf16(double* latDeg, double* lonDeg, + const UWORD* mapcode, enum Territory territory, + MapcodeElements* mapcodeElements) { if ((latDeg == NULL) || (lonDeg == NULL) || (mapcode == NULL)) { return ERR_BAD_ARGUMENTS; - } else { + } + else { enum MapcodeError ret; DecodeRec dec = { - {"", TERRITORY_NONE, "", 0, ""}, - 0, - 0, - 0, - TERRITORY_NONE, - 0, - {0.0, 0.0}, - {0, 0}, - {0.0, 0.0, 0.0, 0.0} + {"", TERRITORY_NONE, "", 0, ""}, + 0, + 0, + 0, + TERRITORY_NONE, + 0, + {0.0, 0.0}, + {0, 0}, + {0.0, 0.0, 0.0, 0.0} }; - dec.orginput = (const char *) mapcode; + dec.orginput = (const char*)mapcode; dec.context = territory; ret = decoderEngine(&dec, FLAG_UTF16_STRING); @@ -3041,7 +3339,7 @@ enum MapcodeError decodeMapcodeToLatLonUtf16(double *latDeg, double *lonDeg, // PUBLIC - encode lat,lon for territory to a mapcode with extraDigits accuracy int -encodeLatLonToSingleMapcode(char *mapcode, double latDeg, double lonDeg, enum Territory territory, int extraDigits) { +encodeLatLonToSingleMapcode(char* mapcode, double latDeg, double lonDeg, enum Territory territory, int extraDigits) { Mapcodes rlocal; int ret; ASSERT(mapcode); @@ -3056,7 +3354,8 @@ encodeLatLonToSingleMapcode(char *mapcode, double latDeg, double lonDeg, enum Te } ret = encodeLatLonToMapcodes_internal(&rlocal, latDeg, lonDeg, territory, 1, DEBUG_STOP_AT, extraDigits); *mapcode = 0; - if (ret <= 0) { // no solutions? + if (ret <= 0) { + // no solutions? return ret; } // prefix territory unless international @@ -3067,7 +3366,8 @@ encodeLatLonToSingleMapcode(char *mapcode, double latDeg, double lonDeg, enum Te // PUBLIC - encode lat,lon for territory to a selected mapcode (from all results) with extraDigits accuracy int -encodeLatLonToSelectedMapcode(char *mapcode, double latDeg, double lonDeg, enum Territory territory, int extraDigits, int indexOfSelected) { +encodeLatLonToSelectedMapcode(char* mapcode, double latDeg, double lonDeg, enum Territory territory, int extraDigits, + int indexOfSelected) { Mapcodes mapcodes; int nrOfResults = 0; nrOfResults = encodeLatLonToMapcodes(&mapcodes, latDeg, lonDeg, territory, extraDigits); @@ -3082,7 +3382,7 @@ encodeLatLonToSelectedMapcode(char *mapcode, double latDeg, double lonDeg, enum // PUBLIC - encode lat,lon for (optional) territory to mapcodes with extraDigits accuracy int -encodeLatLonToMapcodes(Mapcodes *mapcodes, double latDeg, double lonDeg, enum Territory territory, int extraDigits) { +encodeLatLonToMapcodes(Mapcodes* mapcodes, double latDeg, double lonDeg, enum Territory territory, int extraDigits) { ASSERT(mapcodes); if (extraDigits < 0) { return 0; @@ -3100,7 +3400,7 @@ encodeLatLonToMapcodes(Mapcodes *mapcodes, double latDeg, double lonDeg, enum Te /////////////////////////////////////////////////////////////////////////////////////////////// // PUBLIC - returns most common alphabets for territory, NULL if error -const TerritoryAlphabets *getAlphabetsForTerritory(enum Territory territory) { +const TerritoryAlphabets* getAlphabetsForTerritory(enum Territory territory) { if (territory > _TERRITORY_MIN && territory < _TERRITORY_MAX) { return &ALPHABETS_FOR_TERRITORY[INDEX_OF_TERRITORY(territory)]; } @@ -3115,12 +3415,11 @@ const TerritoryAlphabets *getAlphabetsForTerritory(enum Territory territory) { /////////////////////////////////////////////////////////////////////////////////////////////// static int -getFullTerritoryName_internal(char *territoryName, enum Territory territory, int alternative, const char *locale, +getFullTerritoryName_internal(char* territoryName, enum Territory territory, int alternative, const char* locale, enum Alphabet alphabet) { - - const char *territoryNamesPiped; - const char *pipePtr; - const char **territoryNamesList = NULL; + const char* territoryNamesPiped; + const char* pipePtr; + const char** territoryNamesList = NULL; ASSERT(territoryName); ASSERT((_TERRITORY_MIN < territory) && (territory < _TERRITORY_MAX)); @@ -3136,27 +3435,26 @@ getFullTerritoryName_internal(char *territoryName, enum Territory territory, int // Check locale. if (locale == NULL) { - // Use local names if locale is null. territoryNamesList = TERRITORY_FULL_NAME_LOCAL; - } else { - + } + else { // Try and get correct list. int i; - int upTo = (int) strlen(locale); - char localeUpper[4] = ""; // Default locale is empty (which implies 'fallback'). - char *sep = strchr(locale, '_'); // Official separator is '_' (as in "en_US"). + int upTo = (int)strlen(locale); + char localeUpper[4] = ""; // Default locale is empty (which implies 'fallback'). + char* sep = strchr(locale, '_'); // Official separator is '_' (as in "en_US"). if (!sep) { - sep = strchr(locale, '-'); // But we also allow '-' (often used as well). + sep = strchr(locale, '-'); // But we also allow '-' (often used as well). } if (sep) { - upTo = (int) (sep - locale); + upTo = (int)(sep - locale); } lengthCopy(localeUpper, locale, upTo, sizeof(localeUpper)); makeUppercase(localeUpper); territoryNamesList = NULL; - for (i = 0; i < (int) (sizeof(LOCALE_REGISTRY) / sizeof(LOCALE_REGISTRY[0])); ++i) { + for (i = 0; i < (int)(sizeof(LOCALE_REGISTRY) / sizeof(LOCALE_REGISTRY[0])); ++i) { if (!strcmp(LOCALE_REGISTRY[i].locale, localeUpper)) { territoryNamesList = LOCALE_REGISTRY[i].territoryFullNames; break; @@ -3175,18 +3473,20 @@ getFullTerritoryName_internal(char *territoryName, enum Territory territory, int pipePtr = strchr(territoryNamesPiped, '|'); if ((_ALPHABET_MIN < alphabet) && (alphabet < _ALPHABET_MAX)) { - // Alphabet was specified. if (pipePtr) { ASSERT((pipePtr - territoryNamesPiped) <= MAX_TERRITORY_FULLNAME_UTF8_LEN); - lengthCopy(territoryName, territoryNamesPiped, (int) (pipePtr - territoryNamesPiped), + lengthCopy(territoryName, territoryNamesPiped, (int)(pipePtr - territoryNamesPiped), MAX_TERRITORY_FULLNAME_UTF8_LEN); - } else { + } + else { ASSERT(strlen(territoryNamesPiped) <= MAX_TERRITORY_FULLNAME_UTF8_LEN); strcpy(territoryName, territoryNamesPiped); } - if (alphabet != recognizeAlphabetUtf8(territoryName)) { // filter out - if (!pipePtr) { // this is the last string! + if (alphabet != recognizeAlphabetUtf8(territoryName)) { + // filter out + if (!pipePtr) { + // this is the last string! return 0; } territoryNamesPiped = pipePtr + 1; @@ -3194,43 +3494,48 @@ getFullTerritoryName_internal(char *territoryName, enum Territory territory, int } } - if (!pipePtr) { // this is the last string! - if (alternative > 0) { // not what we want? + if (!pipePtr) { + // this is the last string! + if (alternative > 0) { + // not what we want? return 0; } ASSERT(strlen(territoryNamesPiped) <= MAX_TERRITORY_FULLNAME_UTF8_LEN); strcpy(territoryName, territoryNamesPiped); // no bracket, return it all return 1; - } else { - if (!alternative) { // what we want? + } + else { + if (!alternative) { + // what we want? break; } alternative--; territoryNamesPiped = pipePtr + 1; } } - lengthCopy(territoryName, territoryNamesPiped, (int) (pipePtr - territoryNamesPiped), MAX_TERRITORY_FULLNAME_UTF8_LEN); + lengthCopy(territoryName, territoryNamesPiped, (int)(pipePtr - territoryNamesPiped), + MAX_TERRITORY_FULLNAME_UTF8_LEN); return 1; } -int getFullTerritoryNameEnglish(char *territoryName, enum Territory territory, int alternative) { +int getFullTerritoryNameEnglish(char* territoryName, enum Territory territory, int alternative) { ASSERT(territoryName); ASSERT((_TERRITORY_MIN < territory) && (territory < _TERRITORY_MAX)); return getFullTerritoryNameInLocaleUtf8(territoryName, territory, alternative, "en_US"); } -int getFullTerritoryNameInLocaleUtf8(char *territoryName, enum Territory territory, int alternative, - const char *locale) { +int getFullTerritoryNameInLocaleUtf8(char* territoryName, enum Territory territory, int alternative, + const char* locale) { ASSERT(territoryName); ASSERT(((_TERRITORY_MIN < territory) && (territory < _TERRITORY_MAX)) || (territory == TERRITORY_UNKNOWN)); return getFullTerritoryName_internal(territoryName, territory, alternative, locale, _ALPHABET_MIN); } -int getFullTerritoryNameInLocaleInAlphabetUtf8(char *territoryName, enum Territory territory, int alternative, - const char *locale, enum Alphabet alphabet) { +int getFullTerritoryNameInLocaleInAlphabetUtf8(char* territoryName, enum Territory territory, int alternative, + const char* locale, enum Alphabet alphabet) { ASSERT(territoryName); ASSERT((_TERRITORY_MIN < territory) && (territory < _TERRITORY_MAX)); if ((alphabet <= _ALPHABET_MIN) || (alphabet >= _ALPHABET_MAX)) { @@ -3241,14 +3546,14 @@ int getFullTerritoryNameInLocaleInAlphabetUtf8(char *territoryName, enum Territo } -int getFullTerritoryNameLocalUtf8(char *territoryName, enum Territory territory, int alternative) { +int getFullTerritoryNameLocalUtf8(char* territoryName, enum Territory territory, int alternative) { ASSERT(territoryName); ASSERT((_TERRITORY_MIN < territory) && (territory < _TERRITORY_MAX)); return getFullTerritoryName_internal(territoryName, territory, alternative, NULL, _ALPHABET_MIN); } -int getFullTerritoryNameLocalInAlphabetUtf8(char *territoryName, enum Territory territory, int alternative, +int getFullTerritoryNameLocalInAlphabetUtf8(char* territoryName, enum Territory territory, int alternative, enum Alphabet alphabet) { ASSERT(territoryName); ASSERT((_TERRITORY_MIN < territory) && (territory < _TERRITORY_MAX)); @@ -3258,5 +3563,3 @@ int getFullTerritoryNameLocalInAlphabetUtf8(char *territoryName, enum Territory } return getFullTerritoryName_internal(territoryName, territory, alternative, NULL, alphabet); } - - diff --git a/mapcodelib/mapcoder.h b/mapcodelib/mapcoder.h index 8a89461..391b58d 100644 --- a/mapcodelib/mapcoder.h +++ b/mapcodelib/mapcoder.h @@ -1,24 +1,23 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __MAPCODER_H__ #define __MAPCODER_H__ #ifdef __cplusplus extern "C" { + #endif #include "mapcode_territories.h" @@ -62,17 +61,25 @@ extern "C" { #define UWORD unsigned short int // 2-byte unsigned integer. #define MAX_NR_OF_MAPCODE_RESULTS 22 // Max. number of results ever returned by encoder (e.g. for 26.904899, 95.138515). + #define MAX_PRECISION_DIGITS 8 // Max. number of extension characters (excluding the hyphen). Must be even. + #define MAX_PROPER_MAPCODE_ASCII_LEN 11 // Max. chars in a proper mapcode (including the dot, excl. precision extension). + #define MAX_ISOCODE_ASCII_LEN 7 // Max. chars in a valid ISO3166 territory code. #define MAX_CLEAN_MAPCODE_ASCII_LEN (MAX_PROPER_MAPCODE_ASCII_LEN + 1 + MAX_PRECISION_DIGITS) // Max. chars in a clean mapcode (excluding zero-terminator). + #define MAX_MAPCODE_RESULT_ASCII_LEN (MAX_ISOCODE_ASCII_LEN + 1 + MAX_CLEAN_MAPCODE_ASCII_LEN + 1) // Max. chars to store a single result (including zero-terminator). + #define MAX_TERRITORY_FULLNAME_UTF8_LEN 111 // Max. number of characters to store the longest possible territory name (in UTF8) + #define MAX_MAPCODE_RESULT_UTF8_LEN (MAX_MAPCODE_RESULT_ASCII_LEN * 3) // One mapcode character can become at most 3 UTF8characters. + #define MAX_MAPCODE_RESULT_UTF16_LEN (MAX_MAPCODE_RESULT_ASCII_LEN) // Each mapcode character can become one UTF16 word. + // The constants are also exported as variables, to allow other languages to use them. extern char* _MAPCODE_C_VERSION; extern int _MAX_NR_OF_MAPCODE_RESULTS; @@ -92,8 +99,8 @@ extern int _MAX_ALPHABETS_PER_TERRITORY; * International mapcodes never include a territory ISO3166 code, nor a space. */ typedef struct { - int count; // The number of mapcode results (length of array). - char mapcode[MAX_NR_OF_MAPCODE_RESULTS][MAX_MAPCODE_RESULT_ASCII_LEN]; // The mapcodes. + int count; // The number of mapcode results (length of array). + char mapcode[MAX_NR_OF_MAPCODE_RESULTS][MAX_MAPCODE_RESULT_ASCII_LEN]; // The mapcodes. } Mapcodes; @@ -107,11 +114,11 @@ typedef struct { * getTerritoryIsoName(isoName, mapcodeElement.territoryCode, 0) */ typedef struct { - char territoryISO[MAX_ISOCODE_ASCII_LEN + 1]; // The (trimmed and uppercased) territory code, from the input. - enum Territory territoryCode; // The territory, as recognized and disambiguated from territoryISO. - char properMapcode[MAX_PROPER_MAPCODE_ASCII_LEN + 1]; // The (romanised) mapcode excl. territory or extension. - int indexOfDot; // Position of dot in properMapcode (a value between 2 and 5). - char precisionExtension[MAX_PRECISION_DIGITS + 1]; // The (romanised) extension (excluding the hyphen). + char territoryISO[MAX_ISOCODE_ASCII_LEN + 1]; // The (trimmed and uppercased) territory code, from the input. + enum Territory territoryCode; // The territory, as recognized and disambiguated from territoryISO. + char properMapcode[MAX_PROPER_MAPCODE_ASCII_LEN + 1]; // The (romanised) mapcode excl. territory or extension. + int indexOfDot; // Position of dot in properMapcode (a value between 2 and 5). + char precisionExtension[MAX_PRECISION_DIGITS + 1]; // The (romanised) extension (excluding the hyphen). } MapcodeElements; @@ -119,38 +126,37 @@ typedef struct { * List of error return codes (negative except for ERR_OK = 0) */ enum MapcodeError { - // note: an incomplete mapcode could "become" complete by adding letters. - ERR_MAPCODE_INCOMPLETE = -999, // not enough letters (yet) after dot + ERR_MAPCODE_INCOMPLETE = -999, // not enough letters (yet) after dot // format errors. - ERR_ALL_DIGIT_CODE = -299, // mapcode consists only of digits - ERR_INVALID_MAPCODE_FORMAT, // string not recognized as mapcode format - ERR_INVALID_CHARACTER, // mapcode contains an invalid character - ERR_BAD_ARGUMENTS, // an argument is invalid (e.g. NULL) - ERR_INVALID_ENDVOWELS, // mapcodes ends in UE or UU - ERR_EXTENSION_INVALID_LENGTH, // precision extension too long, or empty + ERR_ALL_DIGIT_CODE = -299, // mapcode consists only of digits + ERR_INVALID_MAPCODE_FORMAT, // string not recognized as mapcode format + ERR_INVALID_CHARACTER, // mapcode contains an invalid character + ERR_BAD_ARGUMENTS, // an argument is invalid (e.g. NULL) + ERR_INVALID_ENDVOWELS, // mapcodes ends in UE or UU + ERR_EXTENSION_INVALID_LENGTH, // precision extension too long, or empty ERR_EXTENSION_INVALID_CHARACTER, // bad precision extension character (e.g. Z) - ERR_UNEXPECTED_DOT, // mapcode dot can not be in this position - ERR_DOT_MISSING, // mapcode dot not found - ERR_UNEXPECTED_HYPHEN, // hyphen can not be in this position - ERR_INVALID_VOWEL, // vowel in invalid location, or missing - ERR_BAD_TERRITORY_FORMAT, // mapcode territory badly formatted - ERR_TRAILING_CHARACTERS, // characters found trailing the mapcode + ERR_UNEXPECTED_DOT, // mapcode dot can not be in this position + ERR_DOT_MISSING, // mapcode dot not found + ERR_UNEXPECTED_HYPHEN, // hyphen can not be in this position + ERR_INVALID_VOWEL, // vowel in invalid location, or missing + ERR_BAD_TERRITORY_FORMAT, // mapcode territory badly formatted + ERR_TRAILING_CHARACTERS, // characters found trailing the mapcode // parse errors. - ERR_UNKNOWN_TERRITORY = -199, // mapcode territory not recognized + ERR_UNKNOWN_TERRITORY = -199, // mapcode territory not recognized // other errors. - ERR_BAD_MAPCODE_LENGTH = -99, // proper mapcode too short or too long - ERR_MISSING_TERRITORY, // mapcode can not be decoded without territory - ERR_EXTENSION_UNDECODABLE, // extension does not decode to valid coordinate - ERR_MAPCODE_UNDECODABLE, // mapcode does not decode inside territory - ERR_BAD_COORDINATE, // latitude or longitude is NAN or infinite + ERR_BAD_MAPCODE_LENGTH = -99, // proper mapcode too short or too long + ERR_MISSING_TERRITORY, // mapcode can not be decoded without territory + ERR_EXTENSION_UNDECODABLE, // extension does not decode to valid coordinate + ERR_MAPCODE_UNDECODABLE, // mapcode does not decode inside territory + ERR_BAD_COORDINATE, // latitude or longitude is NAN or infinite // all OK. @@ -178,11 +184,11 @@ enum MapcodeError { */ int encodeLatLonToMapcodes( - Mapcodes *mapcodes, - double latDeg, - double lonDeg, - enum Territory territory, - int extraDigits); + Mapcodes* mapcodes, + double latDeg, + double lonDeg, + enum Territory territory, + int extraDigits); /** @@ -205,11 +211,11 @@ int encodeLatLonToMapcodes( * Number of results. <=0 if encoding failed, or 1 if it succeeded. */ int encodeLatLonToSingleMapcode( - char *mapcode, - double latDeg, - double lonDeg, - enum Territory territory, - int extraDigits); + char* mapcode, + double latDeg, + double lonDeg, + enum Territory territory, + int extraDigits); /** @@ -236,7 +242,7 @@ int encodeLatLonToSingleMapcode( * Total number of results available for selection. <=0 if encoding failed, or >0 if it succeeded. */ int encodeLatLonToSelectedMapcode( - char *mapcode, + char* mapcode, double latDeg, double lonDeg, enum Territory territory, @@ -259,11 +265,11 @@ int encodeLatLonToSelectedMapcode( * ERR_OK if encoding succeeded. */ enum MapcodeError decodeMapcodeToLatLonUtf8( - double *latDeg, - double *lonDeg, - const char *utf8string, - enum Territory territory, - MapcodeElements *mapcodeElements); + double* latDeg, + double* lonDeg, + const char* utf8string, + enum Territory territory, + MapcodeElements* mapcodeElements); /** @@ -281,11 +287,11 @@ enum MapcodeError decodeMapcodeToLatLonUtf8( * ERR_OK if encoding succeeded. */ enum MapcodeError decodeMapcodeToLatLonUtf16( - double *latDeg, - double *lonDeg, - const UWORD *utf16string, - enum Territory territory, - MapcodeElements *mapcodeElements); + double* latDeg, + double* lonDeg, + const UWORD* utf16string, + enum Territory territory, + MapcodeElements* mapcodeElements); /** @@ -303,9 +309,9 @@ enum MapcodeError decodeMapcodeToLatLonUtf16( * NOTE: a correct Mapcode format does not in itself guarantee the mapcode will decode to * a valid coordinate! */ -enum MapcodeError compareWithMapcodeFormatUtf8(const char *utf8String); +enum MapcodeError compareWithMapcodeFormatUtf8(const char* utf8String); -enum MapcodeError compareWithMapcodeFormatUtf16(const UWORD *utf16String); +enum MapcodeError compareWithMapcodeFormatUtf16(const UWORD* utf16String); /** @@ -319,8 +325,8 @@ enum MapcodeError compareWithMapcodeFormatUtf16(const UWORD *utf16String); * Territory (> _TERRITORY_MIN) if succeeded, or TERRITORY_NONE if failed. */ enum Territory getTerritoryCode( - const char *territoryISO, - enum Territory optionalTerritoryContext); + const char* territoryISO, + enum Territory optionalTerritoryContext); /** @@ -334,10 +340,10 @@ enum Territory getTerritoryCode( * Returns: * Pointer to result. String will be empty if territory illegal. */ -char *getTerritoryIsoName( - char *territoryISO, - enum Territory territory, - int useShortName); +char* getTerritoryIsoName( + char* territoryISO, + enum Territory territory, + int useShortName); /** @@ -398,14 +404,14 @@ double maxErrorInMeters(int extraDigits); * Return value: * 0 if coordinate is NOT near more than one territory border, non-0 otherwise. * - * Note that for the mapcode system, the following should hold: IF a point p has a + * Note that for the mapcode system, the following should hold: IF a point p has a * mapcode M, THEN decode(M) delivers a point q within maxErrorInMeters() of p. * Furthermore, encode(q) must yield back M *unless* point q is near multiple borders. */ int multipleBordersNearby( - double latDeg, - double lonDeg, - enum Territory territory); + double latDeg, + double lonDeg, + enum Territory territory); /** @@ -421,9 +427,9 @@ int multipleBordersNearby( * non-0 if the alternative exists (territoryName contains name). */ int getFullTerritoryNameEnglish( - char *territoryName, - enum Territory territory, - int alternative); + char* territoryName, + enum Territory territory, + int alternative); /** @@ -442,15 +448,15 @@ int getFullTerritoryNameEnglish( * non-0 if the alternative exists (territoryName contains name). */ int getFullTerritoryNameLocalUtf8( - char *territoryName, - enum Territory territory, - int alternative); + char* territoryName, + enum Territory territory, + int alternative); int getFullTerritoryNameLocalInAlphabetUtf8( - char *territoryName, - enum Territory territory, - int alternative, - enum Alphabet alphabet); + char* territoryName, + enum Territory territory, + int alternative, + enum Alphabet alphabet); /** @@ -470,17 +476,17 @@ int getFullTerritoryNameLocalInAlphabetUtf8( * non-0 if the alternative exists (territoryName contains name). */ int getFullTerritoryNameInLocaleUtf8( - char *territoryName, - enum Territory territory, - int alternative, - const char *locale); + char* territoryName, + enum Territory territory, + int alternative, + const char* locale); int getFullTerritoryNameInLocaleInAlphabetUtf8( - char *territoryName, - enum Territory territory, - int alternative, - const char *locale, - enum Alphabet alphabet); + char* territoryName, + enum Territory territory, + int alternative, + const char* locale, + enum Alphabet alphabet); /** @@ -506,7 +512,7 @@ typedef struct { * A pointer to a TerritoryAlphabets structure, or NULL if the territory is invalid. * (The pointer is owned by the library and should not be dealloacted by the caller.) */ -const TerritoryAlphabets *getAlphabetsForTerritory(enum Territory territory); +const TerritoryAlphabets* getAlphabetsForTerritory(enum Territory territory); /** @@ -521,7 +527,7 @@ const TerritoryAlphabets *getAlphabetsForTerritory(enum Territory territory); * Returns: * Encode UTF8 string (pointer to utf8String buffer), allocated and deallocated by the caller. */ -char *convertMapcodeToAlphabetUtf8(char *utf8String, const char *asciiString, enum Alphabet alphabet); +char* convertMapcodeToAlphabetUtf8(char* utf8String, const char* asciiString, enum Alphabet alphabet); /** @@ -536,7 +542,7 @@ char *convertMapcodeToAlphabetUtf8(char *utf8String, const char *asciiString, en * Returns: * Encode UTF16 string (pointer to utf16String buffer), allocated and deallocated by the caller. */ -UWORD *convertMapcodeToAlphabetUtf16(UWORD *utf16String, const char *asciiString, enum Alphabet alphabet); +UWORD* convertMapcodeToAlphabetUtf16(UWORD* utf16String, const char* asciiString, enum Alphabet alphabet); #ifdef __cplusplus diff --git a/test/clean.sh b/test/clean.sh index 4651ad2..cf38e5b 100755 --- a/test/clean.sh +++ b/test/clean.sh @@ -1,4 +1,20 @@ #!/bin/sh +# +# Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + echo "Clean test files..." if [ -f unittest.c ] then diff --git a/test/decode_test.h b/test/decode_test.h index 168818e..ef2a12c 100644 --- a/test/decode_test.h +++ b/test/decode_test.h @@ -1,24 +1,22 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // *** GENERATED FILE (dividemaps.cpp), DO NOT CHANGE OR PRETTIFY *** // Generated for data version 2.3.0 using dividemaps.cpp typedef struct { - double latitude; // - double longitude; // + double latitude; // + double longitude; // int nrLocalMapcodes; // 0 if no need to test int nrGlobalMapcodes; // 0 if no need to test const char *mapcode; // expected mapcode, empty if no need to test @@ -15881,4 +15879,3 @@ static const EncodeTestRecord ENCODE_TEST[] = { {40.917864, 79.9773185, 3, 6, "CN-XJ 0123.XX"}, {0, 0, 0, 0, NULL} }; - diff --git a/test/run_all.sh b/test/run_all.sh index 2087305..b2f50db 100755 --- a/test/run_all.sh +++ b/test/run_all.sh @@ -1,4 +1,20 @@ #!/bin/sh +# +# Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + OUT=_report.txt echo "Run all tests..." | tee $OUT date | tee -a $OUT diff --git a/test/run_compare.sh b/test/run_compare.sh index 7b777c2..d69df38 100755 --- a/test/run_compare.sh +++ b/test/run_compare.sh @@ -1,4 +1,20 @@ #!/bin/sh +# +# Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + OPTS="-Wall -Wextra -Wno-pointer-to-int-cast" NEW=../mapcode-2.5.5 diff --git a/test/run_gcov.sh b/test/run_gcov.sh index b508ee6..614c13e 100755 --- a/test/run_gcov.sh +++ b/test/run_gcov.sh @@ -1,4 +1,20 @@ #!/bin/sh +# +# Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + OPTS="-Wall -Wextra -Wno-pointer-to-int-cast -fprofile-arcs -ftest-coverage" LIB="../mapcodelib/mapcoder.o" @@ -28,4 +44,3 @@ gcov unittest.c echo "!! -------------------------------------------------------------" echo "!! Coverage reports in: *.gcov files" echo "!! -------------------------------------------------------------" - diff --git a/test/run_gprof.sh b/test/run_gprof.sh index 4595327..15067bc 100755 --- a/test/run_gprof.sh +++ b/test/run_gprof.sh @@ -1,4 +1,20 @@ #!/bin/sh +# +# Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + OPTS="-Wall -Wextra -Wno-pointer-to-int-cast" LIB="../mapcodelib/mapcoder.o" diff --git a/test/run_normal.sh b/test/run_normal.sh index 33b7c44..662ca18 100755 --- a/test/run_normal.sh +++ b/test/run_normal.sh @@ -1,4 +1,20 @@ #!/bin/sh +# +# Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + OPTS="-Wall -Wextra -Wno-pointer-to-int-cast" echo "!! -------------------------------------------------------------" diff --git a/test/run_sanitizer.sh b/test/run_sanitizer.sh index c301b9b..37718dd 100755 --- a/test/run_sanitizer.sh +++ b/test/run_sanitizer.sh @@ -1,4 +1,20 @@ #!/bin/sh +# +# Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + OPTS="-Wall -Wextra -Wno-pointer-to-int-cast" LIB="../mapcodelib/mapcoder.o" diff --git a/test/run_valgrind.sh b/test/run_valgrind.sh index 20fff57..2c62265 100755 --- a/test/run_valgrind.sh +++ b/test/run_valgrind.sh @@ -1,4 +1,20 @@ #!/bin/sh +# +# Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + OPTS="-Wall -Wextra -Wno-pointer-to-int-cast" LIB="../mapcodelib/mapcoder.o" diff --git a/test/test_territories.h b/test/test_territories.h index 367a17b..67a9cd9 100644 --- a/test/test_territories.h +++ b/test/test_territories.h @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // *** GENERATED FILE (fast_territories.cpp), DO NOT CHANGE OR PRETTIFY *** #include "../mapcodelib/mapcode_territories.h" diff --git a/test/unittest.c b/test/unittest.c index e3c9362..7d88cbf 100644 --- a/test/unittest.c +++ b/test/unittest.c @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. /** * This application performs a number of tests on the Mapcode C library. @@ -1107,13 +1105,13 @@ static int testDistances(void) { 0.11, 0.22, 0.12, 0.2333, 185011466 }; - // check bad values + // check bad values ++nrTests; if (maxErrorInMeters(99) != 0.0) { foundError(); printf("*** ERROR *** maxErrorInMeters(99) = %f (expected 0.0)\n", maxErrorInMeters(99)); } - // check expected values + // check expected values ++nrTests; testDistance(METERS_PER_DEGREE_LON * 1.5, distanceInMeters(0.0, 0.0, 0.0, 1.5)); // Check if #define is correct. ++nrTests; diff --git a/utility/compile.sh b/utility/compile.sh index 2920322..b89dec3 100755 --- a/utility/compile.sh +++ b/utility/compile.sh @@ -1,7 +1,22 @@ #!/bin/sh +# +# Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + echo "Compiling..." cd ../mapcodelib gcc -O3 -c mapcoder.c cd ../utility gcc -O3 mapcode.cpp -lm -o mapcode ../mapcodelib/mapcoder.o - diff --git a/utility/mapcode.cpp b/utility/mapcode.cpp index 332bab5..12f4cef 100644 --- a/utility/mapcode.cpp +++ b/utility/mapcode.cpp @@ -1,18 +1,16 @@ -/* - * Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. /** * This application uses the Mapcode C library to encode and decode Mapcodes. @@ -83,7 +81,7 @@ static double lonLargestNrOfResults = 0.0; * The usage() method explains how this application can be used. It is called * whenever a incorrect amount or combination of parameters is entered. */ -static void usage(const char *appName) { +static void usage(const char* appName) { printf("MAPCODE (version %s)\n", MAPCODE_C_VERSION); printf("Copyright (C) 2014-2017 Stichting Mapcode Foundation\n"); printf("\n"); @@ -168,8 +166,7 @@ static double degToRad(double deg) { * (x, y, z) point on a sphere with a radius of 1. */ static void unitToLatLonDeg( - const double unit1, const double unit2, double *latDeg, double *lonDeg) { - + const double unit1, const double unit2, double* latDeg, double* lonDeg) { // Calculate uniformly distributed 3D Point on sphere (radius = 1.0): // http://mathproofs.blogspot.co.il/2005/04/uniform-random-distribution-on-sphere.html const double theta0 = (2.0 * PI) * unit1; @@ -193,7 +190,7 @@ static void unitToLatLonDeg( * The method convertLatLonToXYZ() convertes a lat/lon pair to a (x, y, z) coordinate * on a sphere with radius 1. */ -static void convertLatLonToXYZ(double latDeg, double lonDeg, double *x, double *y, double *z) { +static void convertLatLonToXYZ(double latDeg, double lonDeg, double* x, double* y, double* z) { double latRad = degToRad(latDeg); double lonRad = degToRad(lonDeg); *x = cos(latRad) * cos(lonRad); @@ -205,7 +202,7 @@ static void convertLatLonToXYZ(double latDeg, double lonDeg, double *x, double * /** * This methods provides a self check for encoding lat/lon to Mapcode. */ -static void selfCheckLatLonToMapcode(const double lat, double lon, const char *mapcode, int extraDigits) { +static void selfCheckLatLonToMapcode(const double lat, double lon, const char* mapcode, int extraDigits) { // TODO: Fix self check; read context. // int context = getTerritoryCode(territory, 0); enum Territory context = TERRITORY_NONE; @@ -223,17 +220,16 @@ static void selfCheckLatLonToMapcode(const double lat, double lon, const char *m } int found = 0; for (int i = 0; !found && (i < nrResults); ++i) { - /* Check if the territory and code were found in results. Note that the territory * may be a minimal code, like IN (which may indicate US-IN or RU-IN). */ - const char *foundMapcode = mapcodes.mapcode[i]; + const char* foundMapcode = mapcodes.mapcode[i]; found = (strcmp(mapcode, foundMapcode) == 0); } if (!found) { fprintf(stderr, "error: encoding lat/lon to mapcode failure; " - "mapcode '%s' decodes to lat=%.20g(%.20g), lon=%.20g(%.20g), " - "which does not encode back to '%s'\n", + "mapcode '%s' decodes to lat=%.20g(%.20g), lon=%.20g(%.20g), " + "which does not encode back to '%s'\n", mapcode, lat, limitLat, lon, limitLon, mapcode); if (selfCheckEnabled) { exit(INTERNAL_ERROR); @@ -246,7 +242,7 @@ static void selfCheckLatLonToMapcode(const double lat, double lon, const char *m /** * This method provides a self-check for decoding a Mapcode to lat/lon. */ -static void selfCheckMapcodeToLatLon(const char *mapcode, +static void selfCheckMapcodeToLatLon(const char* mapcode, const double lat, const double lon) { double foundLat; double foundLon; @@ -269,8 +265,8 @@ static void selfCheckMapcodeToLatLon(const char *mapcode, } if ((deltaLat > DELTA) || (deltaLon > DELTA)) { fprintf(stderr, "error: decoding mapcode to lat/lon failure; " - "lat=%.20g, lon=%.20g produces mapcode %s, " - "which decodes to lat=%.20g (delta=%.20g), lon=%.20g (delta=%.20g)\n", + "lat=%.20g, lon=%.20g produces mapcode %s, " + "which decodes to lat=%.20g (delta=%.20g), lon=%.20g (delta=%.20g)\n", lat, lon, mapcode, foundLat, deltaLat, foundLon, deltaLon); if (selfCheckEnabled) { exit(INTERNAL_ERROR); @@ -280,7 +276,6 @@ static void selfCheckMapcodeToLatLon(const char *mapcode, } static void generateAndOutputMapcodes(double lat, double lon, int iShowError, int extraDigits, int useXYZ) { - enum Territory context = TERRITORY_NONE; while (lon > 180.0) { @@ -311,11 +306,12 @@ static void generateAndOutputMapcodes(double lat, double lon, int iShowError, in double z; convertLatLonToXYZ(lat, lon, &x, &y, &z); printf("%d %.20g %.20g %.20g %.20g %.20g\n", nrResults, lat, lon, x, y, z); - } else { + } + else { printf("%d %.20g %.20g\n", nrResults, lat, lon); } for (int j = 0; j < nrResults; ++j) { - const char *foundMapcode = mapcodes.mapcode[j]; + const char* foundMapcode = mapcodes.mapcode[j]; // Output result line. printf("%s\n", foundMapcode); @@ -358,7 +354,7 @@ static void outputStatistics() { fprintf(stderr, "Total number of 3D points generated = %d\n", totalNrOfPoints); fprintf(stderr, "Total number of mapcodes generated = %d\n", totalNrOfResults); fprintf(stderr, "Average number of mapcodes per 3D Point = %.20g\n", - ((float) totalNrOfResults) / ((float) totalNrOfPoints)); + ((float)totalNrOfResults) / ((float)totalNrOfPoints)); fprintf(stderr, "Largest number of results for 1 mapcode = %d at (%.20g, %.20g)\n", largestNrOfResults, latLargestNrOfResults, lonLargestNrOfResults); } @@ -369,7 +365,7 @@ static void outputStatistics() { */ static void showProgress(int i) { fprintf(stderr, "[%d%%] Processed %d of %d regions (generated %d mapcodes)...\r", - (int) ((((float) i / ((float) totalNrOfPoints)) * 100.0) + 0.5), + (int)((((float)i / ((float)totalNrOfPoints)) * 100.0) + 0.5), i, totalNrOfPoints, totalNrOfResults); } @@ -377,7 +373,7 @@ static void showProgress(int i) { * This is the main() method which is called from the command-line. * Return code 0 means success. Any other values means some sort of error occurred. */ -int main(const int argc, const char **argv) { +int main(const int argc, const char** argv) { // Assume no extra digits (unless overridden later. int extraDigits = 0; @@ -385,7 +381,7 @@ int main(const int argc, const char **argv) { int useXYZ = 0; // Provide usage message if no arguments specified. - const char *appName = argv[0]; + const char* appName = argv[0]; selfCheckEnabled = (strstr(appName, "debug") != 0); if (selfCheckEnabled) { fprintf(stderr, "(debug mode: self checking enabled)\n"); @@ -396,9 +392,8 @@ int main(const int argc, const char **argv) { } // First argument: command. - const char *cmd = argv[1]; + const char* cmd = argv[1]; if ((strcmp(cmd, "-d") == 0) || (strcmp(cmd, "--decode") == 0)) { - // ------------------------------------------------------------------ // Decode: [-d | --decode] [ ...] // ------------------------------------------------------------------ @@ -408,7 +403,7 @@ int main(const int argc, const char **argv) { return NORMAL_ERROR; } - const char *defaultTerritory = argv[2]; + const char* defaultTerritory = argv[2]; double lat; double lon; @@ -417,9 +412,8 @@ int main(const int argc, const char **argv) { // Decode every Mapcode. for (int i = 3; i < argc; ++i) { - // Decode the Mapcode to a lat/lon. - const char *mapcode = argv[i]; + const char* mapcode = argv[i]; int err = decodeMapcodeToLatLonUtf8(&lat, &lon, mapcode, context, NULL); if (err != 0) { fprintf(stderr, "error: cannot decode '%s %s'\n", defaultTerritory, mapcode); @@ -431,25 +425,25 @@ int main(const int argc, const char **argv) { // Self-checking code to see if encoder produces this Mapcode for the lat/lon. if (selfCheckEnabled) { - const char *suffix = strstr(mapcode, "-"); + const char* suffix = strstr(mapcode, "-"); extraDigits = 0; if (suffix != 0) { - extraDigits = (int) (strlen(suffix) - 1); + extraDigits = (int)(strlen(suffix) - 1); } selfCheckLatLonToMapcode(lat, lon, mapcode, extraDigits); } } - } else if ((strcmp(cmd, "-e") == 0) || (strcmp(cmd, "-e0") == 0) || - (strcmp(cmd, "-e1") == 0) || (strcmp(cmd, "-e2") == 0) || - (strcmp(cmd, "-e3") == 0) || (strcmp(cmd, "-e4") == 0) || - (strcmp(cmd, "-e5") == 0) || (strcmp(cmd, "-e6") == 0) || - (strcmp(cmd, "-e7") == 0) || (strcmp(cmd, "-e8") == 0) || - (strcmp(cmd, "--encode") == 0) || (strcmp(cmd, "--encode0") == 0) || - (strcmp(cmd, "--encode1") == 0) || (strcmp(cmd, "--encode2") == 0) || - (strcmp(cmd, "--encode3") == 0) || (strcmp(cmd, "--encode4") == 0) || - (strcmp(cmd, "--encode5") == 0) || (strcmp(cmd, "--encode6") == 0) || - (strcmp(cmd, "--encode7") == 0) || (strcmp(cmd, "--encode8") == 0)) { - + } + else if ((strcmp(cmd, "-e") == 0) || (strcmp(cmd, "-e0") == 0) || + (strcmp(cmd, "-e1") == 0) || (strcmp(cmd, "-e2") == 0) || + (strcmp(cmd, "-e3") == 0) || (strcmp(cmd, "-e4") == 0) || + (strcmp(cmd, "-e5") == 0) || (strcmp(cmd, "-e6") == 0) || + (strcmp(cmd, "-e7") == 0) || (strcmp(cmd, "-e8") == 0) || + (strcmp(cmd, "--encode") == 0) || (strcmp(cmd, "--encode0") == 0) || + (strcmp(cmd, "--encode1") == 0) || (strcmp(cmd, "--encode2") == 0) || + (strcmp(cmd, "--encode3") == 0) || (strcmp(cmd, "--encode4") == 0) || + (strcmp(cmd, "--encode5") == 0) || (strcmp(cmd, "--encode6") == 0) || + (strcmp(cmd, "--encode7") == 0) || (strcmp(cmd, "--encode8") == 0)) { // ------------------------------------------------------------------ // Encode: [-e[0-8] | --encode[0-8]] [territory]> // ------------------------------------------------------------------ @@ -468,27 +462,35 @@ int main(const int argc, const char **argv) { if (strstr(cmd, "-e1") || strstr(cmd, "--encode1")) { extraDigits = 1; - } else if (strstr(cmd, "-e2") || strstr(cmd, "--encode2")) { + } + else if (strstr(cmd, "-e2") || strstr(cmd, "--encode2")) { extraDigits = 2; - } else if (strstr(cmd, "-e3") || strstr(cmd, "--encode3")) { + } + else if (strstr(cmd, "-e3") || strstr(cmd, "--encode3")) { extraDigits = 3; - } else if (strstr(cmd, "-e4") || strstr(cmd, "--encode4")) { + } + else if (strstr(cmd, "-e4") || strstr(cmd, "--encode4")) { extraDigits = 4; - } else if (strstr(cmd, "-e5") || strstr(cmd, "--encode5")) { + } + else if (strstr(cmd, "-e5") || strstr(cmd, "--encode5")) { extraDigits = 5; - } else if (strstr(cmd, "-e6") || strstr(cmd, "--encode6")) { + } + else if (strstr(cmd, "-e6") || strstr(cmd, "--encode6")) { extraDigits = 6; - } else if (strstr(cmd, "-e7") || strstr(cmd, "--encode7")) { + } + else if (strstr(cmd, "-e7") || strstr(cmd, "--encode7")) { extraDigits = 7; - } else if (strstr(cmd, "-e8") || strstr(cmd, "--encode8")) { + } + else if (strstr(cmd, "-e8") || strstr(cmd, "--encode8")) { extraDigits = 8; - } else { + } + else { extraDigits = 0; } // Get territory context. enum Territory context = TERRITORY_NONE; - const char *defaultTerritory = "AAA"; + const char* defaultTerritory = "AAA"; if (argc == 5) { context = getTerritoryCode(argv[4], TERRITORY_NONE); defaultTerritory = argv[4]; @@ -505,7 +507,7 @@ int main(const int argc, const char **argv) { // Output the Mapcode. for (int i = 0; i < nrResults; ++i) { - const char *foundMapcode = mapcodes.mapcode[i]; + const char* foundMapcode = mapcodes.mapcode[i]; printf("%s\n", foundMapcode); // Self-checking code to see if decoder produces the lat/lon for all of these Mapcodes. @@ -513,9 +515,9 @@ int main(const int argc, const char **argv) { selfCheckMapcodeToLatLon(foundMapcode, lat, lon); } } - } else if ((strcmp(cmd, "-t") == 0) || - (strcmp(cmd, "--territories") == 0)) { - + } + else if ((strcmp(cmd, "-t") == 0) || + (strcmp(cmd, "--territories") == 0)) { // ------------------------------------------------------------------ // Generate a test set based on the Mapcode territories // ------------------------------------------------------------------ @@ -526,7 +528,7 @@ int main(const int argc, const char **argv) { } printf("ccode,territorycodes(pipe-separated),alphabets(pipe-seperated),names(pipe-separated)\n"); for (int i = _TERRITORY_MIN + 1; i < _TERRITORY_MAX; ++i) { - const enum Territory ccode = (enum Territory) i; + const enum Territory ccode = (enum Territory)i; char territoryName[MAX_MAPCODE_RESULT_ASCII_LEN]; printf("%d,", INDEX_OF_TERRITORY(i)); @@ -537,7 +539,7 @@ int main(const int argc, const char **argv) { char fullcode[16]; strcpy(fullcode, ALPHA_SEARCH[a].alphaCode); if (fullcode[0] >= '0' && fullcode[0] <= '9') { - static const char *parents2 = "US,IN,CA,AU,MX,BR,RU,CN,"; + static const char* parents2 = "US,IN,CA,AU,MX,BR,RU,CN,"; int p = (fullcode[0] - '0'); memcpy(fullcode, &parents2[p * 3 - 3], 2); fullcode[2] = '-'; @@ -551,7 +553,7 @@ int main(const int argc, const char **argv) { printf(","); // Print alphabets. - const TerritoryAlphabets *territoryAlphabets = getAlphabetsForTerritory(ccode); + const TerritoryAlphabets* territoryAlphabets = getAlphabetsForTerritory(ccode); for (int j = 0; j < territoryAlphabets->count; j++) { if (j > 0) { printf("|"); @@ -561,13 +563,13 @@ int main(const int argc, const char **argv) { printf(","); // Use internal knowledge of TERRITORY_FULL_NAME to show aliases of full territory name. - char *names = strdup(TERRITORY_FULL_NAME_EN[INDEX_OF_TERRITORY(ccode)]); - char *s = names; + char* names = strdup(TERRITORY_FULL_NAME_EN[INDEX_OF_TERRITORY(ccode)]); + char* s = names; while (s) { if (s != names) { printf("|"); } - char *e = strstr(s, " ("); + char* e = strstr(s, " ("); if (e) { *e = 0; if (e[-1] == ')') { @@ -575,7 +577,8 @@ int main(const int argc, const char **argv) { } printf("%s", s); s = e + 2; - } else { + } + else { e = s + strlen(s); if (e[-1] == ')') { e[-1] = 0; @@ -586,77 +589,77 @@ int main(const int argc, const char **argv) { } printf("\n"); } - } else if ((strcmp(cmd, "-a") == 0) || - (strcmp(cmd, "--alphabets") == 0)) { - + } + else if ((strcmp(cmd, "-a") == 0) || + (strcmp(cmd, "--alphabets") == 0)) { // ------------------------------------------------------------------ // Generate a test set based on the Mapcode territories // ------------------------------------------------------------------ - static const char *mapcodeForCSV[] = { - // all characters - "89.EU", - "00.0A", - "BCDF.GHJK", - "LMNP.QRST", - "VWXY.Z123", - "4567.890B", - // all forms - "pq.xy", - "pq.xyz", - "pqx.yz", - "pq.rxyz", - "pqr.xyz", - "pqrx.yz", - "pqr.sxyz", - "pqrs.xyz", - "pqrs.txyz", - "pqrst.vxyz", - // all adjad forms - "p1.xy", - "pq.2y", - "3q.x4", - "5q.6y", - "pq.1yz", - "pq1.yz", - "p2.x3z", - "p2x.3z", - "pq.1xy2", - "pq1.xy2", - "pq1x.y2", - "p3.rx4z", - "p3r.x4z", - "p3rx.4z", - "5q.r6y7", - "5qr.6y7", - "5qr6.y7", - "pq1.sx2z", - "pq1s.x2z", - "p3r.s4yz", - "p3rs.4yz", - "5qr.6xy7", - "5qr6.xy7", - "8q9.sx0z", - "8q9s.x0z", - "1qr2.tx3z", - "p4rs.5xy6", - "p7r8.t9y0", - "pq1st.2xy3", - "p4rs5.vx6z", - "7qr8t.v9yz", - "p1r2t.3x4z", - "5q6s7.v8y9", - // non-mapcode - "^0123456789!@#$^&*()/:;[]{}<>?|~%", - "abcdefghijklmnopqrstuvwxyz", - "ABCDEFGHIJKLMNOPQRSTUVWXYZ", - // special case for digit-like characters - "OI.xx", - "oi.xx", - "oi.xx-oooooooo", - "oi.xx-iiiiiiii", - "PQ.RS-01234567", - "PQ.RS-890", - NULL + static const char* mapcodeForCSV[] = { + // all characters + "89.EU", + "00.0A", + "BCDF.GHJK", + "LMNP.QRST", + "VWXY.Z123", + "4567.890B", + // all forms + "pq.xy", + "pq.xyz", + "pqx.yz", + "pq.rxyz", + "pqr.xyz", + "pqrx.yz", + "pqr.sxyz", + "pqrs.xyz", + "pqrs.txyz", + "pqrst.vxyz", + // all adjad forms + "p1.xy", + "pq.2y", + "3q.x4", + "5q.6y", + "pq.1yz", + "pq1.yz", + "p2.x3z", + "p2x.3z", + "pq.1xy2", + "pq1.xy2", + "pq1x.y2", + "p3.rx4z", + "p3r.x4z", + "p3rx.4z", + "5q.r6y7", + "5qr.6y7", + "5qr6.y7", + "pq1.sx2z", + "pq1s.x2z", + "p3r.s4yz", + "p3rs.4yz", + "5qr.6xy7", + "5qr6.xy7", + "8q9.sx0z", + "8q9s.x0z", + "1qr2.tx3z", + "p4rs.5xy6", + "p7r8.t9y0", + "pq1st.2xy3", + "p4rs5.vx6z", + "7qr8t.v9yz", + "p1r2t.3x4z", + "5q6s7.v8y9", + // non-mapcode + "^0123456789!@#$^&*()/:;[]{}<>?|~%", + "abcdefghijklmnopqrstuvwxyz", + "ABCDEFGHIJKLMNOPQRSTUVWXYZ", + // special case for digit-like characters + "OI.xx", + "oi.xx", + "oi.xx-oooooooo", + "oi.xx-iiiiiiii", + "PQ.RS-01234567", + "PQ.RS-890", + NULL }; if ((argc < 2) || (argc > 2)) { fprintf(stderr, "error: incorrect number of arguments\n\n"); @@ -666,7 +669,7 @@ int main(const int argc, const char **argv) { printf("alphabetNr,MapcodeInRoman,MapcodeInAlphabet\n"); for (enum Alphabet alphabet = ALPHABET_ROMAN; - alphabet < _ALPHABET_MAX; alphabet = (enum Alphabet) (alphabet + 1)) { + alphabet < _ALPHABET_MAX; alphabet = (enum Alphabet)(alphabet + 1)) { int variant; for (variant = 0; variant <= 2; variant++) { int m; @@ -678,7 +681,7 @@ int main(const int argc, const char **argv) { strcpy(mapcode, mapcodeForCSV[m]); strcat(mapcode, (variant == 1) ? "-bc" : (variant == 2) ? "-DFGHJKLM" : ""); for (i = 0; mapcode[i]; ++i) { - mapcode[i] = (char) toupper((int) mapcode[i]); + mapcode[i] = (char)toupper((int)mapcode[i]); } // convert to alphabet, and back to roman convertMapcodeToAlphabetUtf8(asciiString, mapcode, alphabet); @@ -687,9 +690,9 @@ int main(const int argc, const char **argv) { } } } - } else if ((strcmp(cmd, "-b") == 0) || (strcmp(cmd, "-bXYZ") == 0) || - (strcmp(cmd, "--boundaries") == 0) || (strcmp(cmd, "--boundariesXYZ") == 0)) { - + } + else if ((strcmp(cmd, "-b") == 0) || (strcmp(cmd, "-bXYZ") == 0) || + (strcmp(cmd, "--boundaries") == 0) || (strcmp(cmd, "--boundariesXYZ") == 0)) { // ------------------------------------------------------------------ // Generate a test set based on the Mapcode boundaries. // ------------------------------------------------------------------ @@ -717,11 +720,11 @@ int main(const int argc, const char **argv) { double lat; double lon; - const TerritoryBoundary *mm = TERRITORY_BOUNDARY(i); - minLon = ((double) mm->minx) / 1.0E6; - maxLon = ((double) mm->maxx) / 1.0E6; - minLat = ((double) mm->miny) / 1.0E6; - maxLat = ((double) mm->maxy) / 1.0E6; + const TerritoryBoundary* mm = TERRITORY_BOUNDARY(i); + minLon = ((double)mm->minx) / 1.0E6; + maxLon = ((double)mm->maxx) / 1.0E6; + minLat = ((double)mm->miny) / 1.0E6; + maxLat = ((double)mm->maxy) / 1.0E6; // Try center. lat = (maxLat - minLat) / 2.0; @@ -752,11 +755,11 @@ int main(const int argc, const char **argv) { } } outputStatistics(); - } else if ((strcmp(cmd, "-g") == 0) || (strcmp(cmd, "-gXYZ") == 0) || - (strcmp(cmd, "--grid") == 0) || (strcmp(cmd, "--gridXYZ") == 0) || - (strcmp(cmd, "-r") == 0) || (strcmp(cmd, "-rXYZ") == 0) || - (strcmp(cmd, "--random") == 0) || (strcmp(cmd, "--randomXYZ") == 0)) { - + } + else if ((strcmp(cmd, "-g") == 0) || (strcmp(cmd, "-gXYZ") == 0) || + (strcmp(cmd, "--grid") == 0) || (strcmp(cmd, "--gridXYZ") == 0) || + (strcmp(cmd, "-r") == 0) || (strcmp(cmd, "-rXYZ") == 0) || + (strcmp(cmd, "--random") == 0) || (strcmp(cmd, "--randomXYZ") == 0)) { // ------------------------------------------------------------------ // Generate grid test set: [-g | --grid] [] // Generate uniform test set: [-r | --random] [] @@ -784,9 +787,10 @@ int main(const int argc, const char **argv) { if (random) { if (argc == 5) { const int seed = atoi(argv[4]); - srand((unsigned int) seed); - } else { - srand((unsigned int) time(0)); + srand((unsigned int)seed); + } + else { + srand((unsigned int)time(0)); } } useXYZ = (strstr(cmd, "XYZ") != 0); @@ -804,15 +808,17 @@ int main(const int argc, const char **argv) { double unit2; if (random) { - unit1 = ((double) rand()) / RAND_MAX; - unit2 = ((double) rand()) / RAND_MAX; - } else { - unit1 = ((double) gridX) / line; - unit2 = ((double) gridY) / line; + unit1 = ((double)rand()) / RAND_MAX; + unit2 = ((double)rand()) / RAND_MAX; + } + else { + unit1 = ((double)gridX) / line; + unit2 = ((double)gridY) / line; if (gridX < line) { ++gridX; - } else { + } + else { gridX = 0; ++gridY; } @@ -826,8 +832,8 @@ int main(const int argc, const char **argv) { } } outputStatistics(); - } else { - + } + else { // ------------------------------------------------------------------ // Usage. // ------------------------------------------------------------------ From 22e61af320b5fbde1a3010fd702df35c02d056b7 Mon Sep 17 00:00:00 2001 From: Rijn Buve Date: Wed, 10 Sep 2025 10:33:52 +0200 Subject: [PATCH 09/31] Updated to 2025 copyrights and small clean up --- CMakeLists.txt | 2 +- NOTICE | 2 +- README.md | 10 +++++++--- mapcodelib/mapcoder.h | 2 +- test/no_warnings.env | 1 + test/run_compare.sh | 16 +++++++++++++--- test/run_gcov.sh | 2 +- test/run_gprof.sh | 2 +- test/run_normal.sh | 2 +- test/run_sanitizer.sh | 2 +- test/run_valgrind.sh | 2 +- utility/mapcode.cpp | 2 +- 12 files changed, 30 insertions(+), 15 deletions(-) create mode 100755 test/no_warnings.env diff --git a/CMakeLists.txt b/CMakeLists.txt index 32492c3..fccca4c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) +# Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/NOTICE b/NOTICE index 1bac6aa..d7561a4 100644 --- a/NOTICE +++ b/NOTICE @@ -1,4 +1,4 @@ Original C library created by Pieter Geelen. Work on Java version of the Mapcode library by Rijn Buve (original port by Matthew Lowden). -Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com) +Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com) diff --git a/README.md b/README.md index 088069d..8e178f7 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Codacy Badge](https://api.codacy.com/project/badge/Grade/d2e3d7a469484bfd8b801ce94d3f1737)](https://www.codacy.com/app/rijnb/mapcode-cpp?utm_source=github.com&utm_medium=referral&utm_content=mapcode-foundation/mapcode-cpp&utm_campaign=Badge_Grade) [![License](http://img.shields.io/badge/license-APACHE2-blue.svg)]() -**Copyright (C) 2014-2017 Stichting Mapcode Foundation (http://www.mapcode.com)** +**Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com)** **Online documentation: http://mapcode-foundation.github.io/mapcode-cpp/** @@ -64,8 +64,8 @@ decode Mapcodes. This produces the following help text: - MAPCODE (version 2.5.5) - Copyright (C) 2014-2017 Stichting Mapcode Foundation + MAPCODE (version 2.5.6) + Copyright (C) 2014-2025 Stichting Mapcode Foundation Usage: mapcode [-d| --decode] [ ...] @@ -243,6 +243,10 @@ let fullName = String.init(cString: buffer) ## Release Notes +### 2.5.6 + +* Updated to latest compilers. + ### 2.5.4 - 2.5.5 * Added `encodeLatLonToSelectedMapcode` as a convenience for languages that use the diff --git a/mapcodelib/mapcoder.h b/mapcodelib/mapcoder.h index 391b58d..69bf2c0 100644 --- a/mapcodelib/mapcoder.h +++ b/mapcodelib/mapcoder.h @@ -57,7 +57,7 @@ extern "C" { #define MAPCODE_SUPPORT_LANGUAGE_UK #endif -#define MAPCODE_C_VERSION "2.5.5" +#define MAPCODE_C_VERSION "2.5.6" #define UWORD unsigned short int // 2-byte unsigned integer. #define MAX_NR_OF_MAPCODE_RESULTS 22 // Max. number of results ever returned by encoder (e.g. for 26.904899, 95.138515). diff --git a/test/no_warnings.env b/test/no_warnings.env new file mode 100755 index 0000000..8dd1a54 --- /dev/null +++ b/test/no_warnings.env @@ -0,0 +1 @@ +-Wall -Wextra -Wno-pointer-to-int-cast -Wno-deprecated-declarations -Wno-unused-but-set-variable diff --git a/test/run_compare.sh b/test/run_compare.sh index d69df38..6222bdd 100755 --- a/test/run_compare.sh +++ b/test/run_compare.sh @@ -15,9 +15,8 @@ # limitations under the License. # -OPTS="-Wall -Wextra -Wno-pointer-to-int-cast" - -NEW=../mapcode-2.5.5 +OPTS="$(cat ./no_warnings.env)" +NEW=../mapcode-2.5.6 OLD=../mapcode-2.5.2 NEWFILE=_new.txt OLDFILE=_old.txt @@ -26,10 +25,20 @@ OPTS1="--grid 1000000 8" OPTS2="--random 1000000 8 1234" OPTS3="--random 1000000 8 11223344" +comparison_error() { + echo "If you want to run the comparison test script, please create symbolic links" + echo "to two version of the mapcode utility:" + echo " $OLD" + echo " $NEW" + echo "These must be placed in the root directory of this project." + exit +} + TEST=$(which $OLD) if [ "$TEST" = "" ] then echo "No $OLD found on this machine - skipping script..." + comparison_error exit 1 fi @@ -50,6 +59,7 @@ TEST=$(which $NEW) if [ "$TEST" = "" ] then echo "No $NEW found on this machine - skipping script..." + comparison_error exit 1 fi diff --git a/test/run_gcov.sh b/test/run_gcov.sh index 614c13e..39b6537 100755 --- a/test/run_gcov.sh +++ b/test/run_gcov.sh @@ -15,7 +15,7 @@ # limitations under the License. # -OPTS="-Wall -Wextra -Wno-pointer-to-int-cast -fprofile-arcs -ftest-coverage" +OPTS="$(cat ./no_warnings.env) -fprofile-arcs -ftest-coverage" LIB="../mapcodelib/mapcoder.o" TEST=$(which gcov) diff --git a/test/run_gprof.sh b/test/run_gprof.sh index 15067bc..d880a54 100755 --- a/test/run_gprof.sh +++ b/test/run_gprof.sh @@ -15,7 +15,7 @@ # limitations under the License. # -OPTS="-Wall -Wextra -Wno-pointer-to-int-cast" +OPTS="$(cat ./no_warnings.env)" LIB="../mapcodelib/mapcoder.o" TEST=$(which gprof) diff --git a/test/run_normal.sh b/test/run_normal.sh index 662ca18..954fe51 100755 --- a/test/run_normal.sh +++ b/test/run_normal.sh @@ -15,7 +15,7 @@ # limitations under the License. # -OPTS="-Wall -Wextra -Wno-pointer-to-int-cast" +OPTS="$(cat ./no_warnings.env)" echo "!! -------------------------------------------------------------" echo "Run normal..." diff --git a/test/run_sanitizer.sh b/test/run_sanitizer.sh index 37718dd..f282712 100755 --- a/test/run_sanitizer.sh +++ b/test/run_sanitizer.sh @@ -15,7 +15,7 @@ # limitations under the License. # -OPTS="-Wall -Wextra -Wno-pointer-to-int-cast" +OPTS="$(cat ./no_warnings.env)" LIB="../mapcodelib/mapcoder.o" export ASAN_OPTIONS=debug=true:strict_string_checks=1:detect_stack_use_after_return=true:detect_invalid_pointer_pairs=99999:detect_container_overflow=true:detect_odr_violation=2:check_initialization_order=true:strict_init_order=true diff --git a/test/run_valgrind.sh b/test/run_valgrind.sh index 2c62265..6444247 100755 --- a/test/run_valgrind.sh +++ b/test/run_valgrind.sh @@ -15,7 +15,7 @@ # limitations under the License. # -OPTS="-Wall -Wextra -Wno-pointer-to-int-cast" +OPTS="$(cat ./no_warnings.env)" LIB="../mapcodelib/mapcoder.o" TEST=$(which valgrind) diff --git a/utility/mapcode.cpp b/utility/mapcode.cpp index 12f4cef..05641f3 100644 --- a/utility/mapcode.cpp +++ b/utility/mapcode.cpp @@ -83,7 +83,7 @@ static double lonLargestNrOfResults = 0.0; */ static void usage(const char* appName) { printf("MAPCODE (version %s)\n", MAPCODE_C_VERSION); - printf("Copyright (C) 2014-2017 Stichting Mapcode Foundation\n"); + printf("Copyright (C) 2014-2025 Stichting Mapcode Foundation\n"); printf("\n"); printf("Usage:\n"); From 5826d8d5b129a767286b66e45bef9c8a3e6222ed Mon Sep 17 00:00:00 2001 From: Rijn Buve Date: Wed, 10 Sep 2025 11:55:58 +0200 Subject: [PATCH 10/31] Simplify code --- mapcodelib/mapcoder.c | 100 +++++++++++++++++++++++++++--------------- 1 file changed, 64 insertions(+), 36 deletions(-) diff --git a/mapcodelib/mapcoder.c b/mapcodelib/mapcoder.c index 8a577b8..cfecda9 100644 --- a/mapcodelib/mapcoder.c +++ b/mapcodelib/mapcoder.c @@ -128,6 +128,19 @@ void _TestAssert(int iCondition, const char* cstrFile, int iLine) { #define MICROLAT_TO_FRACTIONS_FACTOR ((double) MAX_PRECISION_FACTOR) #define MICROLON_TO_FRACTIONS_FACTOR (4.0 * MAX_PRECISION_FACTOR) +// Grid and encoding constants +#define GRID_SIZE_31 31 // Base grid size for encoding +#define GRID_SIZE_961 961 // 31 * 31 - large grid size +#define GRID_SIZE_962 962 // 961 + 1 +#define GRID_SIZE_SQUARED (961 * 961) // Square of grid size +#define MAX_NAMELESS_RECORDS 62 // Maximum number of nameless records +#define Y_DIVIDER 90 // Standard Y coordinate divider +#define SPECIAL_CODEX_21 21 // Special codex identifier +#define SPECIAL_CODEX_22 22 // Special codex identifier +#define SPECIAL_CODEX_13 13 // Special codex identifier +#define SPECIAL_CODEX_14 14 // Special codex identifier +#define GRID_MULTIPLIER_16 16 // Grid calculation multiplier + #define FLAG_UTF8_STRING 0 // interpret pointer a utf8 characters #define FLAG_UTF16_STRING 1 // interpret pointer a UWORD* to utf16 characters @@ -1054,6 +1067,44 @@ static void encodeGrid(char* result, const EncodeRec* enc, const int m, const in } +/** + * Helper function to calculate storage offset for nameless encoding. + * Determines the storage offset based on the number of nameless records (A), + * the current record index (X), and the codex value (codexm). + * This complex calculation was extracted to improve readability of encodeNameless. + */ +static int calculateStorageOffset(int A, int X, int codexm) { + const int p = GRID_SIZE_31 / A; + const int r = GRID_SIZE_31 % A; // the first r items are p+1 + + if (codexm != SPECIAL_CODEX_21 && A <= GRID_SIZE_31) { + return (X * p + (X < r ? X : r)) * GRID_SIZE_SQUARED; + } + else if (codexm != SPECIAL_CODEX_21 && A < MAX_NAMELESS_RECORDS) { + if (X < (MAX_NAMELESS_RECORDS - A)) { + return X * GRID_SIZE_SQUARED; + } + else { + int storage_offset = (MAX_NAMELESS_RECORDS - A + ((X - MAX_NAMELESS_RECORDS + A) / 2)) * GRID_SIZE_SQUARED; + if ((X + A) & 1) { + storage_offset += (GRID_MULTIPLIER_16 * GRID_SIZE_961 * GRID_SIZE_31); + } + return storage_offset; + } + } + else { + const int BASEPOWER = (codexm == SPECIAL_CODEX_21) ? GRID_SIZE_SQUARED : GRID_SIZE_SQUARED * GRID_SIZE_31; + int BASEPOWERA = (BASEPOWER / A); + if (A == MAX_NAMELESS_RECORDS) { + BASEPOWERA++; + } + else { + BASEPOWERA = GRID_SIZE_961 * (BASEPOWERA / GRID_SIZE_961); + } + return X * BASEPOWERA; + } +} + // *result==0 in case of error static void encodeNameless(char* result, const EncodeRec* enc, const enum Territory ccode, const int extraDigits, const int m) { @@ -1067,45 +1118,16 @@ static void encodeNameless(char* result, const EncodeRec* enc, const enum Territ *result = 0; { - const int p = 31 / A; - const int r = 31 % A; // the first r items are p+1 const int codexm = coDex(m); const int codexlen = (codexm / 10) + (codexm % 10); // determine side of square around centre int SIDE; - int storage_offset; + const int storage_offset = calculateStorageOffset(A, X, codexm); const TerritoryBoundary* b; int xSIDE, orgSIDE; - if (codexm != 21 && A <= 31) { - storage_offset = (X * p + (X < r ? X : r)) * (961 * 961); - } - else if (codexm != 21 && A < 62) { - if (X < (62 - A)) { - storage_offset = X * (961 * 961); - } - else { - storage_offset = (62 - A + ((X - 62 + A) / 2)) * (961 * 961); - if ((X + A) & 1) { - storage_offset += (16 * 961 * 31); - } - } - } - else { - const int BASEPOWER = (codexm == 21) ? 961 * 961 : 961 * 961 * 31; - int BASEPOWERA = (BASEPOWER / A); - if (A == 62) { - BASEPOWERA++; - } - else { - BASEPOWERA = (961) * (BASEPOWERA / 961); - } - - storage_offset = X * BASEPOWERA; - } - SIDE = SMART_DIV(m); ASSERT(SIDE > 0); @@ -1120,7 +1142,7 @@ static void encodeNameless(char* result, const EncodeRec* enc, const enum Territ const int dx = (4 * (enc->coord32.lonMicroDeg - b->minx) + xFracture) / dividerx4; // div with quarters const int extrax4 = (enc->coord32.lonMicroDeg - b->minx) * 4 - (dx * dividerx4); // mod with quarters - const int dividery = 90; + const int dividery = Y_DIVIDER; int dy = (b->maxy - enc->coord32.latMicroDeg) / dividery; int extray = (b->maxy - enc->coord32.latMicroDeg) % dividery; @@ -1130,7 +1152,7 @@ static void encodeNameless(char* result, const EncodeRec* enc, const enum Territ } if (IS_SPECIAL_SHAPE(m)) { - SIDE = 1 + ((b->maxy - b->miny) / 90); // new side, based purely on y-distance + SIDE = 1 + ((b->maxy - b->miny) / Y_DIVIDER); // new side, based purely on y-distance xSIDE = (orgSIDE * orgSIDE) / SIDE; v += encodeSixWide(dx, SIDE - 1 - dy, xSIDE, SIDE); } @@ -1141,7 +1163,7 @@ static void encodeNameless(char* result, const EncodeRec* enc, const enum Territ encodeBase31(result, v, codexlen + 1); // nameless { int dotp = codexlen; - if (codexm == 13) { + if (codexm == SPECIAL_CODEX_13) { dotp--; } memmove(result + dotp, result + dotp - 1, 4); @@ -1149,7 +1171,7 @@ static void encodeNameless(char* result, const EncodeRec* enc, const enum Territ } if (!IS_SPECIAL_SHAPE(m)) { - if (codexm == 22 && A < 62 && orgSIDE == 961) { + if (codexm == SPECIAL_CODEX_22 && A < MAX_NAMELESS_RECORDS && orgSIDE == GRID_SIZE_961) { const char t = result[codexlen - 2]; result[codexlen - 2] = result[codexlen]; result[codexlen] = t; @@ -2416,7 +2438,12 @@ enum MapcodeError compareWithMapcodeFormatUtf16(const UWORD* Utf16String) { } -// returns nonzero if error +/** + * Main decoder engine that converts a mapcode string to coordinates. + * Handles territory context, validates format, and iterates through + * possible territory boundaries to find the correct decoding. + * Returns ERR_OK on success, or appropriate error code on failure. + */ static enum MapcodeError decoderEngine(DecodeRec* dec, int parseFlags) { enum Territory ccode; enum MapcodeError err; @@ -2428,9 +2455,10 @@ static enum MapcodeError decoderEngine(DecodeRec* dec, int parseFlags) { int wasAllDigits = 0; ASSERT(dec); + // Parse the mapcode string into its components (territory, proper mapcode, extension) err = parseMapcodeString(&dec->mapcodeElements, dec->orginput, parseFlags, dec->context); if (err) { - // clear all parsed fields in case of error + // Clear all parsed fields in case of error to ensure clean state dec->mapcodeElements.territoryISO[0] = 0; dec->mapcodeElements.properMapcode[0] = 0; dec->mapcodeElements.precisionExtension[0] = 0; From 74c2fda9e60143498c99f1108a7a85b72cadea17 Mon Sep 17 00:00:00 2001 From: Rijn Buve Date: Wed, 10 Sep 2025 12:13:52 +0200 Subject: [PATCH 11/31] Added comments (using AI). --- mapcodelib/mapcoder.c | 510 +++++++++++++++++++++++++++++------------- 1 file changed, 359 insertions(+), 151 deletions(-) diff --git a/mapcodelib/mapcoder.c b/mapcodelib/mapcoder.c index cfecda9..a495957 100644 --- a/mapcodelib/mapcoder.c +++ b/mapcodelib/mapcoder.c @@ -12,6 +12,30 @@ // See the License for the specific language governing permissions and // limitations under the License. +/** + * @file mapcoder.c + * @brief Core implementation of the Mapcode encoding and decoding system + * + * This file contains the complete implementation of the Mapcode system, which provides + * a way to encode any location on Earth into a short alphanumeric code and decode + * it back to precise coordinates. + * + * Key functionality includes: + * - Encoding latitude/longitude coordinates to mapcode strings + * - Decoding mapcode strings back to coordinates + * - Territory-based encoding for shorter codes within specific regions + * - Support for high-precision encoding with extra digits + * - Multi-alphabet support for international usage + * - Territory name handling and lookup functions + * + * The encoding uses a sophisticated grid system that divides the Earth's surface + * into increasingly fine grids, with special handling for different territory + * shapes and boundary conditions. + * + * @author Stichting Mapcode Foundation + * @version See MAPCODE_C_VERSION constant + */ + #include // strlen strcpy strcat memcpy memmove strstr strchr memcmp #include // atof #include // toupper @@ -51,24 +75,42 @@ #include "internal_territory_names_tr.h" #include "internal_territory_names_uk.h" -// The constants are also exported as variables, to allow other languages to use them. -char* _MAPCODE_C_VERSION = MAPCODE_C_VERSION; -int _MAX_NR_OF_MAPCODE_RESULTS = MAX_NR_OF_MAPCODE_RESULTS; -int _MAX_PRECISION_DIGITS = MAX_PRECISION_DIGITS; -int _MAX_PROPER_MAPCODE_ASCII_LEN = MAX_PROPER_MAPCODE_ASCII_LEN; -int _MAX_ISOCODE_ASCII_LEN = MAX_ISOCODE_ASCII_LEN; -int _MAX_CLEAN_MAPCODE_ASCII_LEN = MAX_CLEAN_MAPCODE_ASCII_LEN; -int _MAX_MAPCODE_RESULT_ASCII_LEN = MAX_MAPCODE_RESULT_ASCII_LEN; -int _MAX_TERRITORY_FULLNAME_UTF8_LEN = MAX_TERRITORY_FULLNAME_UTF8_LEN; -int _MAX_MAPCODE_RESULT_UTF8_LEN = MAX_MAPCODE_RESULT_UTF8_LEN; -int _MAX_MAPCODE_RESULT_UTF16_LEN = MAX_MAPCODE_RESULT_UTF16_LEN; -int _MAX_ALPHABETS_PER_TERRITORY = MAX_ALPHABETS_PER_TERRITORY; +/** + * @section exported_constants Exported Constants + * The constants are also exported as variables to allow other languages to use them. + * This provides runtime access to compile-time constants for language bindings. + */ +char* _MAPCODE_C_VERSION = MAPCODE_C_VERSION; // Version string of the mapcode library +int _MAX_NR_OF_MAPCODE_RESULTS = MAX_NR_OF_MAPCODE_RESULTS; // Maximum number of mapcode results returned +int _MAX_PRECISION_DIGITS = MAX_PRECISION_DIGITS; // Maximum extra precision digits supported +int _MAX_PROPER_MAPCODE_ASCII_LEN = MAX_PROPER_MAPCODE_ASCII_LEN; // Maximum length of a proper mapcode in ASCII +int _MAX_ISOCODE_ASCII_LEN = MAX_ISOCODE_ASCII_LEN; // Maximum length of ISO territory code +int _MAX_CLEAN_MAPCODE_ASCII_LEN = MAX_CLEAN_MAPCODE_ASCII_LEN; // Maximum length of clean mapcode (no territory) +int _MAX_MAPCODE_RESULT_ASCII_LEN = MAX_MAPCODE_RESULT_ASCII_LEN; // Maximum length of complete result string +int _MAX_TERRITORY_FULLNAME_UTF8_LEN = MAX_TERRITORY_FULLNAME_UTF8_LEN; // Maximum territory name length in UTF-8 +int _MAX_MAPCODE_RESULT_UTF8_LEN = MAX_MAPCODE_RESULT_UTF8_LEN; // Maximum result length in UTF-8 +int _MAX_MAPCODE_RESULT_UTF16_LEN = MAX_MAPCODE_RESULT_UTF16_LEN; // Maximum result length in UTF-16 +int _MAX_ALPHABETS_PER_TERRITORY = MAX_ALPHABETS_PER_TERRITORY; // Maximum alphabets supported per territory +/** + * @section debug_system Debug and Assertion System + * Debug mode provides runtime assertion checking to catch programming errors + * during development and testing. + */ #ifdef DEBUG #include - +/** + * @brief Debug assertion function that tracks and reports failed conditions + * @param iCondition The condition to test (should be true) + * @param cstrFile Source file where assertion occurred + * @param iLine Line number where assertion occurred + * + * This function provides detailed error reporting when assertions fail in debug mode. + * It keeps track of assertion failures and terminates the program after too many + * failures to prevent cascading errors. + */ void _TestAssert(int iCondition, const char* cstrFile, int iLine) { static int nrAsserts = 0; if (!iCondition) { @@ -81,9 +123,10 @@ void _TestAssert(int iCondition, const char* cstrFile, int iLine) { } } - +// In debug mode, assertions are active and report failures #define ASSERT(condition) _TestAssert((int) (condition), __FILE__, (int) __LINE__) #else +// In release mode, assertions are compiled out for performance #define ASSERT(condition) #endif @@ -98,120 +141,217 @@ void _TestAssert(int iCondition, const char* cstrFile, int iLine) { #endif -#define IS_NAMELESS(m) (TERRITORY_BOUNDARIES[m].flags & 64) -#define IS_RESTRICTED(m) (TERRITORY_BOUNDARIES[m].flags & 512) -#define IS_SPECIAL_SHAPE(m) (TERRITORY_BOUNDARIES[m].flags & 1024) -#define REC_TYPE(m) ((TERRITORY_BOUNDARIES[m].flags >> 7) & 3) -#define SMART_DIV(m) (TERRITORY_BOUNDARIES[m].flags >> 16) -#define HEADER_LETTER(m) (ENCODE_CHARS[(TERRITORY_BOUNDARIES[m].flags >> 11) & 31]) - -#define TOKENSEP 0 -#define TOKENDOT 1 -#define TOKENCHR 2 -#define TOKENVOWEL 3 -#define TOKENZERO 4 -#define TOKENHYPH 5 - -#define STATE_GO 31 - -#define MATH_PI 3.14159265358979323846 -#define MAX_PRECISION_FACTOR 810000 // 30 to the power (MAX_PRECISION_DIGITS/2). - -// Radius of Earth. -#define EARTH_RADIUS_X_METERS 6378137 -#define EARTH_RADIUS_Y_METERS 6356752 - -// Circumference of Earth. -#define EARTH_CIRCUMFERENCE_X (EARTH_RADIUS_X_METERS * 2 * MATH_PI) -#define EARTH_CIRCUMFERENCE_Y (EARTH_RADIUS_Y_METERS * 2 * MATH_PI) - -#define MICROLAT_TO_FRACTIONS_FACTOR ((double) MAX_PRECISION_FACTOR) -#define MICROLON_TO_FRACTIONS_FACTOR (4.0 * MAX_PRECISION_FACTOR) - -// Grid and encoding constants -#define GRID_SIZE_31 31 // Base grid size for encoding -#define GRID_SIZE_961 961 // 31 * 31 - large grid size -#define GRID_SIZE_962 962 // 961 + 1 -#define GRID_SIZE_SQUARED (961 * 961) // Square of grid size -#define MAX_NAMELESS_RECORDS 62 // Maximum number of nameless records -#define Y_DIVIDER 90 // Standard Y coordinate divider -#define SPECIAL_CODEX_21 21 // Special codex identifier -#define SPECIAL_CODEX_22 22 // Special codex identifier -#define SPECIAL_CODEX_13 13 // Special codex identifier -#define SPECIAL_CODEX_14 14 // Special codex identifier -#define GRID_MULTIPLIER_16 16 // Grid calculation multiplier - -#define FLAG_UTF8_STRING 0 // interpret pointer a utf8 characters -#define FLAG_UTF16_STRING 1 // interpret pointer a UWORD* to utf16 characters - -// Meters per degree latitude is fixed. For longitude: use factor * cos(midpoint of two degree latitudes). -static const double METERS_PER_DEGREE_LAT = EARTH_CIRCUMFERENCE_Y / 360.0; -static const double METERS_PER_DEGREE_LON = EARTH_CIRCUMFERENCE_X / 360.0; - -static const int DEBUG_STOP_AT = -1; // to externally test-restrict internal encoding, do not use! +/** + * @section territory_flag_macros Territory Boundary Flag Extraction Macros + * These macros extract specific information from the flags field of territory boundary records. + * Each territory has flags that encode various properties used during encoding/decoding. + */ +#define IS_NAMELESS(m) (TERRITORY_BOUNDARIES[m].flags & 64) // Territory uses nameless encoding (bit 6) +#define IS_RESTRICTED(m) (TERRITORY_BOUNDARIES[m].flags & 512) // Territory has access restrictions (bit 9) +#define IS_SPECIAL_SHAPE(m) (TERRITORY_BOUNDARIES[m].flags & 1024) // Territory has non-standard shape (bit 10) +#define REC_TYPE(m) ((TERRITORY_BOUNDARIES[m].flags >> 7) & 3) // Record type (bits 7-8): grid encoding method +#define SMART_DIV(m) (TERRITORY_BOUNDARIES[m].flags >> 16) // Smart divider value (bits 16+): grid subdivision +#define HEADER_LETTER(m) (ENCODE_CHARS[(TERRITORY_BOUNDARIES[m].flags >> 11) & 31]) // Header letter for encoding (bits 11-15) + +/** + * @section parsing_tokens Token Types for Mapcode Parsing + * These constants define different types of tokens encountered during mapcode parsing. + */ +#define TOKENSEP 0 // Separator character (space, hyphen, etc.) +#define TOKENDOT 1 // Dot character '.' +#define TOKENCHR 2 // Regular character (consonant or digit) +#define TOKENVOWEL 3 // Vowel character (A, E, U) +#define TOKENZERO 4 // Zero digit '0' +#define TOKENHYPH 5 // Hyphen character '-' +/** + * @section parsing_state Parsing State Constants + */ +#define STATE_GO 31 // Active parsing state identifier + +/** + * @section mathematical_constants Mathematical and Earth Constants + * Fundamental constants used in coordinate calculations and grid mathematics. + */ +#define MATH_PI 3.14159265358979323846 // High-precision value of π +#define MAX_PRECISION_FACTOR 810000 // 30^(MAX_PRECISION_DIGITS/2) - base precision factor + +// Earth's radius in meters (WGS84 ellipsoid approximation) +#define EARTH_RADIUS_X_METERS 6378137 // Equatorial radius (semi-major axis) +#define EARTH_RADIUS_Y_METERS 6356752 // Polar radius (semi-minor axis) + +// Earth's circumference in meters +#define EARTH_CIRCUMFERENCE_X (EARTH_RADIUS_X_METERS * 2 * MATH_PI) // Equatorial circumference +#define EARTH_CIRCUMFERENCE_Y (EARTH_RADIUS_Y_METERS * 2 * MATH_PI) // Meridional circumference + +/** + * @section coordinate_conversion Coordinate Conversion Factors + * Factors for converting between different coordinate representations. + */ +#define MICROLAT_TO_FRACTIONS_FACTOR ((double) MAX_PRECISION_FACTOR) // Convert latitude microdegrees to fractions +#define MICROLON_TO_FRACTIONS_FACTOR (4.0 * MAX_PRECISION_FACTOR) // Convert longitude microdegrees to fractions + +/** + * @section grid_encoding_constants Grid and Encoding Constants + * These constants define the fundamental grid structure used in mapcode encoding. + * The mapcode system uses a base-31 grid system that recursively subdivides space. + */ +#define GRID_SIZE_31 31 // Base grid size: 31x31 grid cells (base-31 encoding) +#define GRID_SIZE_961 961 // 31^2 = 961 - second level grid size +#define GRID_SIZE_962 962 // 961 + 1 - used for boundary calculations +#define GRID_SIZE_SQUARED (961 * 961) // 961^2 - third level grid size for fine precision +#define MAX_NAMELESS_RECORDS 62 // Maximum nameless territory records in subdivision +#define Y_DIVIDER 90 // Standard latitude divider for grid calculations +#define SPECIAL_CODEX_21 21 // Special encoding method identifier (type 21) +#define SPECIAL_CODEX_22 22 // Special encoding method identifier (type 22) +#define SPECIAL_CODEX_13 13 // Special encoding method identifier (type 13) +#define SPECIAL_CODEX_14 14 // Special encoding method identifier (type 14) +#define GRID_MULTIPLIER_16 16 // Multiplier for grid offset calculations + +/** + * @section string_encoding_flags String Encoding Type Flags + * Flags to distinguish between different string encoding formats. + */ +#define FLAG_UTF8_STRING 0 // Interpret string pointer as UTF-8 characters +#define FLAG_UTF16_STRING 1 // Interpret string pointer as UTF-16 characters + +/** + * @section distance_calculation Distance Calculation Constants + * Meters per degree latitude is constant globally. For longitude, the actual + * distance varies by latitude, so use factor * cos(midpoint_latitude). + */ +static const double METERS_PER_DEGREE_LAT = EARTH_CIRCUMFERENCE_Y / 360.0; // ~111,319 meters per degree latitude +static const double METERS_PER_DEGREE_LON = EARTH_CIRCUMFERENCE_X / 360.0; // ~111,320 meters per degree longitude at equator + +/** + * @section debug_control Debug Control Variables + */ +static const int DEBUG_STOP_AT = -1; // Internal debug limit for encoding tests (do not use in production!) + +/** + * @section locale_support Locale and Language Support Structures + * These structures support multi-language territory names for international usage. + */ + +/** + * @brief Registry item linking a locale identifier to territory name translations + * @param locale Two-character locale identifier (e.g., "EN", "FR", "DE") + * @param territoryFullNames Array of translated territory names for this locale + */ typedef struct { - const char* locale; - const char** territoryFullNames; + const char* locale; // Language/locale identifier + const char** territoryFullNames; // Array of territory names in this language } LocaleRegistryItem; +/** + * @brief Registry of supported locales and their territory name translations + * This array maps two-character locale codes to arrays of translated territory names. + * Supports major world languages for international mapcode applications. + */ static const LocaleRegistryItem LOCALE_REGISTRY[] = { - {"AF", TERRITORY_FULL_NAME_AF}, - {"AR", TERRITORY_FULL_NAME_AR}, - {"BE", TERRITORY_FULL_NAME_BE}, - {"CN", TERRITORY_FULL_NAME_CN}, - {"CS", TERRITORY_FULL_NAME_CS}, - {"DA", TERRITORY_FULL_NAME_DA}, - {"DE", TERRITORY_FULL_NAME_DE}, - {"EN", TERRITORY_FULL_NAME_EN}, - {"ES", TERRITORY_FULL_NAME_ES}, - {"FI", TERRITORY_FULL_NAME_FI}, - {"FR", TERRITORY_FULL_NAME_FR}, - {"HE", TERRITORY_FULL_NAME_HE}, - {"HI", TERRITORY_FULL_NAME_HI}, - {"HR", TERRITORY_FULL_NAME_HR}, - {"ID", TERRITORY_FULL_NAME_ID}, - {"IT", TERRITORY_FULL_NAME_IT}, - {"JA", TERRITORY_FULL_NAME_JA}, - {"KO", TERRITORY_FULL_NAME_KO}, - {"NL", TERRITORY_FULL_NAME_NL}, - {"NO", TERRITORY_FULL_NAME_NO}, - {"PT", TERRITORY_FULL_NAME_PT}, - {"PL", TERRITORY_FULL_NAME_PL}, - {"RU", TERRITORY_FULL_NAME_RU}, - {"SV", TERRITORY_FULL_NAME_SV}, - {"SW", TERRITORY_FULL_NAME_SW}, - {"TR", TERRITORY_FULL_NAME_TR}, - {"UK", TERRITORY_FULL_NAME_UK} + {"AF", TERRITORY_FULL_NAME_AF}, // Afrikaans + {"AR", TERRITORY_FULL_NAME_AR}, // Arabic + {"BE", TERRITORY_FULL_NAME_BE}, // Belarusian + {"CN", TERRITORY_FULL_NAME_CN}, // Chinese (Simplified) + {"CS", TERRITORY_FULL_NAME_CS}, // Czech + {"DA", TERRITORY_FULL_NAME_DA}, // Danish + {"DE", TERRITORY_FULL_NAME_DE}, // German + {"EN", TERRITORY_FULL_NAME_EN}, // English + {"ES", TERRITORY_FULL_NAME_ES}, // Spanish + {"FI", TERRITORY_FULL_NAME_FI}, // Finnish + {"FR", TERRITORY_FULL_NAME_FR}, // French + {"HE", TERRITORY_FULL_NAME_HE}, // Hebrew + {"HI", TERRITORY_FULL_NAME_HI}, // Hindi + {"HR", TERRITORY_FULL_NAME_HR}, // Croatian + {"ID", TERRITORY_FULL_NAME_ID}, // Indonesian + {"IT", TERRITORY_FULL_NAME_IT}, // Italian + {"JA", TERRITORY_FULL_NAME_JA}, // Japanese + {"KO", TERRITORY_FULL_NAME_KO}, // Korean + {"NL", TERRITORY_FULL_NAME_NL}, // Dutch + {"NO", TERRITORY_FULL_NAME_NO}, // Norwegian + {"PT", TERRITORY_FULL_NAME_PT}, // Portuguese + {"PL", TERRITORY_FULL_NAME_PL}, // Polish + {"RU", TERRITORY_FULL_NAME_RU}, // Russian + {"SV", TERRITORY_FULL_NAME_SV}, // Swedish + {"SW", TERRITORY_FULL_NAME_SW}, // Swahili + {"TR", TERRITORY_FULL_NAME_TR}, // Turkish + {"UK", TERRITORY_FULL_NAME_UK} // Ukrainian }; -// important information about the 8 parents +/** + * @section parent_territories Parent Territory Information + * The 8 major parent territories that contain subdivisions with shorter mapcodes. + * These large countries/regions are subdivided to allow shorter codes within them. + */ + +/** + * @brief Three-letter ISO codes for parent territories + * Comma-separated string of 3-letter ISO codes for the 8 major parent territories + */ static const char* PARENTS_3 = "USA,IND,CAN,AUS,MEX,BRA,RUS,CHN,"; + +/** + * @brief Two-letter ISO codes for parent territories + * Comma-separated string of 2-letter ISO codes corresponding to PARENTS_3 + */ static const char* PARENTS_2 = "US,IN,CA,AU,MX,BR,RU,CN,"; + +/** + * @brief Territory enumeration values for parent territories + * Array mapping parent territory numbers (1-8) to their Territory enum values. + * Index 0 is TERRITORY_NONE, indices 1-8 are the 8 parent territories. + */ static const enum Territory PARENT_NR[9] = { - TERRITORY_NONE, - TERRITORY_USA, - TERRITORY_IND, - TERRITORY_CAN, - TERRITORY_AUS, - TERRITORY_MEX, - TERRITORY_BRA, - TERRITORY_RUS, - TERRITORY_CHN + TERRITORY_NONE, // 0 - no parent + TERRITORY_USA, // 1 - United States of America + TERRITORY_IND, // 2 - India + TERRITORY_CAN, // 3 - Canada + TERRITORY_AUS, // 4 - Australia + TERRITORY_MEX, // 5 - Mexico + TERRITORY_BRA, // 6 - Brazil + TERRITORY_RUS, // 7 - Russia + TERRITORY_CHN // 8 - China }; -// base-31 alphabet, digits (0-9), consonants (10-30), vowels (31-33) +/** + * @section base31_encoding Base-31 Encoding Character Set + * The mapcode system uses a base-31 alphabet for compact encoding. + * Character set is designed to avoid ambiguous characters and improve readability. + */ + +/** + * @brief Base-31 encoding alphabet used for mapcode generation + * + * Character mapping: + * - Positions 0-9: Digits '0'-'9' + * - Positions 10-30: Consonants 'B','C','D','F','G','H','J','K','L','M','N','P','Q','R','S','T','V','W','X','Y','Z' + * - Positions 31-33: Vowels 'A','E','U' (used for special purposes) + * + * Notable exclusions: 'I' and 'O' are excluded to avoid confusion with '1' and '0' + */ static const char ENCODE_CHARS[34] = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', - 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z', - 'A', 'E', 'U' + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', // Digits (0-9) + 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', // Consonants (10-19) + 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z', // Consonants (20-30) + 'A', 'E', 'U' // Vowels (31-33) }; +/** + * @brief Convert ASCII character to base-31 value for mapcode decoding + * @param ch ASCII character to decode + * @return Base-31 value (0-30), or negative for special cases: + * -1: illegal character + * -2: vowel 'A' (position 31 in ENCODE_CHARS) + * -3: vowel 'E' (position 32 in ENCODE_CHARS) + * -4: vowel 'U' (position 33 in ENCODE_CHARS) + * + * Special handling: 'O'/'o' maps to '0', 'I'/'i' maps to '1' for user convenience. + * This allows users to type potentially confusing characters and still get correct results. + */ static signed char decodeChar(const char ch) { - // base-31 value of ascii character (negative for illegal characters) - // special cases -2, -3, -4 for vowels; o and i interpreted as 0 and 1. + // Lookup table for base-31 value of ASCII character (negative for illegal characters) + // Special cases -2, -3, -4 for vowels; 'O' and 'I' interpreted as '0' and '1'. static const signed char decode_chars[256] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16 @@ -233,13 +373,25 @@ static signed char decodeChar(const char ch) { return decode_chars[(unsigned char)ch]; // ch can be negative, must be fit to range 0-255. } -/////////////////////////////////////////////////////////////////////////////////////////////// -// -// distanceInMeters -// -/////////////////////////////////////////////////////////////////////////////////////////////// - -// PUBLIC - returns distance (in meters) between two coordinates (in degrees) +/** + * @brief Calculate the distance in meters between two coordinate points + * @param latDeg1 Latitude of first point in degrees (-90 to 90) + * @param lonDeg1 Longitude of first point in degrees (-180 to 180) + * @param latDeg2 Latitude of second point in degrees (-90 to 90) + * @param lonDeg2 Longitude of second point in degrees (-180 to 180) + * @return Distance between the two points in meters + * + * This function calculates the approximate distance between two points on Earth's surface + * using a simplified spherical model. The calculation: + * 1. Handles longitude wrapping around the 180°/-180° meridian + * 2. Uses cosine correction for longitude distance based on average latitude + * 3. Applies Pythagorean theorem to get final distance + * + * Note: This is an approximation suitable for mapcode purposes but not a precise + * geodetic distance calculation (which would require ellipsoid math). + * + * @public + */ double distanceInMeters(double latDeg1, double lonDeg1, double latDeg2, double lonDeg2) { double dx; double dy; @@ -263,27 +415,49 @@ double distanceInMeters(double latDeg1, double lonDeg1, double latDeg2, double l return sqrt(dx * dx + dy * dy); } -/////////////////////////////////////////////////////////////////////////////////////////////// -// -// maxErrorInMeters -// -/////////////////////////////////////////////////////////////////////////////////////////////// +/** + * @section precision_error_calculation Precision Error Calculation + * The mapcode system supports high-precision encoding with additional digits. + * Each extra digit reduces the maximum possible error by approximately a factor of 5. + */ -// maximum error in meters for a certain nr of high-precision digits +/** + * @brief Maximum error distances in meters for different precision levels + * + * This lookup table contains the maximum possible error (in meters) for each + * supported precision level. The values are empirically determined based on + * the mapcode grid system: + * + * Index 0: Base precision (no extra digits) - ~7.49m max error + * Index 1: 1 extra digit - ~1.39m max error + * Index 2: 2 extra digits - ~0.251m max error + * ... and so on up to MAX_PRECISION_DIGITS + * + * Each additional precision level reduces error by approximately 30^0.5 ≈ 5.48x + */ static const double MAX_ERROR_IN_METERS[MAX_PRECISION_DIGITS + 1] = { - 7.49, - 1.39, - 0.251, - 0.0462, - 0.00837, - 0.00154, - 0.000279, - 0.0000514, - 0.0000093 + 7.49, // 0 extra digits - base precision + 1.39, // 1 extra digit + 0.251, // 2 extra digits + 0.0462, // 3 extra digits + 0.00837, // 4 extra digits + 0.00154, // 5 extra digits + 0.000279, // 6 extra digits + 0.0000514, // 7 extra digits + 0.0000093 // 8 extra digits (maximum supported) }; - -// PUBLIC - returns maximum error in meters for a certain nr of high-precision digits +/** + * @brief Get the maximum possible error for a given precision level + * @param extraDigits Number of extra precision digits (0 to MAX_PRECISION_DIGITS) + * @return Maximum error in meters for this precision level, or 0.0 for invalid input + * + * This function returns the theoretical maximum error that can occur when encoding + * a coordinate at the specified precision level. The actual error is typically + * much smaller than this maximum value. + * + * @public + */ double maxErrorInMeters(int extraDigits) { ASSERT(extraDigits >= 0); if ((extraDigits < 0) || (extraDigits > MAX_PRECISION_DIGITS)) { @@ -292,36 +466,70 @@ double maxErrorInMeters(int extraDigits) { return MAX_ERROR_IN_METERS[extraDigits]; } -/////////////////////////////////////////////////////////////////////////////////////////////// -// -// Point / Point32 -// -/////////////////////////////////////////////////////////////////////////////////////////////// +/** + * @section coordinate_structures Coordinate Representation Structures + * The mapcode system uses multiple coordinate representations for different purposes: + * - Point32: Integer microdegree coordinates (precise, efficient) + * - Point: Floating-point coordinates (flexible units depending on context) + */ +/** + * @brief Integer coordinate point in microdegrees + * + * This structure represents coordinates using 32-bit integers in microdegrees + * (millionths of a degree). This provides: + * - High precision without floating-point rounding errors + * - Efficient storage and comparison operations + * - Range: approximately ±2147 degrees (more than sufficient for Earth coordinates) + */ typedef struct { - int latMicroDeg; // latitude in microdegrees - int lonMicroDeg; // longitude in microdegrees + int latMicroDeg; // Latitude in microdegrees (±90,000,000 for ±90°) + int lonMicroDeg; // Longitude in microdegrees (±180,000,000 for ±180°) } Point32; +/** + * @brief Floating-point coordinate point with flexible units + * + * This structure uses double-precision floating-point numbers. + * The units depend on context: + * - Sometimes degrees (for user-facing coordinates) + * - Sometimes fractions (for internal grid calculations) + * - Sometimes other units (for intermediate calculations) + */ typedef struct { - // Point - double lat; // latitude (units depend on situation) - double lon; // longitude (units depend on situation) + double lat; // Latitude (units depend on usage context) + double lon; // Longitude (units depend on usage context) } Point; - +/** + * @brief Convert fraction coordinates to 32-bit microdegree coordinates + * @param p Point containing coordinates in fraction units + * @return Point32 with coordinates converted to microdegrees + * + * This function converts from the internal fraction coordinate system + * to microdegree integers. The conversion factors (810000, 3240000) are + * derived from the mapcode grid mathematics. + */ static Point32 convertFractionsToCoord32(const Point* p) { Point32 p32; - p32.latMicroDeg = (int)floor(p->lat / 810000); - p32.lonMicroDeg = (int)floor(p->lon / 3240000); + p32.latMicroDeg = (int)floor(p->lat / 810000); // Convert latitude fractions to microdegrees + p32.lonMicroDeg = (int)floor(p->lon / 3240000); // Convert longitude fractions to microdegrees return p32; } - +/** + * @brief Convert fraction coordinates to degree coordinates + * @param p Point containing coordinates in fraction units + * @return Point with coordinates converted to degrees + * + * This function converts from the internal fraction coordinate system + * to standard degree coordinates. The large divisors (810000 * 1000000, + * 3240000 * 1000000) scale from fractions to degrees. + */ static Point convertFractionsToDegrees(const Point* p) { Point pd; - pd.lat = p->lat / (810000 * 1000000.0); - pd.lon = p->lon / (3240000 * 1000000.0); + pd.lat = p->lat / (810000 * 1000000.0); // Convert latitude fractions to degrees + pd.lon = p->lon / (3240000 * 1000000.0); // Convert longitude fractions to degrees return pd; } From 0b729486470ad711efa220a0577425e3fd11042c Mon Sep 17 00:00:00 2001 From: Rijn Buve Date: Thu, 11 Sep 2025 10:07:27 +0200 Subject: [PATCH 12/31] Fix comments --- mapcodelib/mapcoder.h | 75 +++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 39 deletions(-) diff --git a/mapcodelib/mapcoder.h b/mapcodelib/mapcoder.h index 69bf2c0..63e68b1 100644 --- a/mapcodelib/mapcoder.h +++ b/mapcodelib/mapcoder.h @@ -72,12 +72,12 @@ extern "C" { #define MAX_MAPCODE_RESULT_ASCII_LEN (MAX_ISOCODE_ASCII_LEN + 1 + MAX_CLEAN_MAPCODE_ASCII_LEN + 1) // Max. chars to store a single result (including zero-terminator). -#define MAX_TERRITORY_FULLNAME_UTF8_LEN 111 // Max. number of characters to store the longest possible territory name (in UTF8) +#define MAX_TERRITORY_FULLNAME_UTF8_LEN 111 // Max. number of characters to store the longest possible territory name (in UTF-8) -#define MAX_MAPCODE_RESULT_UTF8_LEN (MAX_MAPCODE_RESULT_ASCII_LEN * 3) // One mapcode character can become at most 3 UTF8characters. +#define MAX_MAPCODE_RESULT_UTF8_LEN (MAX_MAPCODE_RESULT_ASCII_LEN * 3) // One mapcode character can become at most 3 UTF-8 characters. -#define MAX_MAPCODE_RESULT_UTF16_LEN (MAX_MAPCODE_RESULT_ASCII_LEN) // Each mapcode character can become one UTF16 word. +#define MAX_MAPCODE_RESULT_UTF16_LEN (MAX_MAPCODE_RESULT_ASCII_LEN) // Each mapcode character can become one UTF-16 word. // The constants are also exported as variables, to allow other languages to use them. @@ -94,8 +94,8 @@ extern int _MAX_MAPCODE_RESULT_UTF16_LEN; extern int _MAX_ALPHABETS_PER_TERRITORY; /** - * The type Mapcodes hold a number of mapcodes, for example from an encoding call. - * If a result contains a space, that space seperates the territory ISO3166 code from the mapcode. + * The type `Mapcodes` holds a number of Mapcodes, for example from an encoding call. + * If a result contains a space, that space separates the territory ISO3166 code from the mapcode. * International mapcodes never include a territory ISO3166 code, nor a space. */ typedef struct { @@ -105,13 +105,13 @@ typedef struct { /** - * The MapcodeElement structure is returned by decodeXXX and can be used to inspect or clean up the - * mapcode input. The field territoryISO contains the cleaned up territory code from the input, but + * The `MapcodeElements` structure is returned by decodeXXX and can be used to inspect or clean up the + * mapcode input. The field `territoryISO` contains the cleaned-up territory code from the input, but * the code may be abbreviated, or even missing (if it wasn't available in the input). * * If you want to get a full territory code, use: - * char isoName[MAX_ISOCODE_ASCII_LEN + 1]; - * getTerritoryIsoName(isoName, mapcodeElement.territoryCode, 0) + * char isoName[MAX_ISOCODE_ASCII_LEN + 1]; + * getTerritoryIsoName(isoName, mapcodeElements.territoryCode, 0); */ typedef struct { char territoryISO[MAX_ISOCODE_ASCII_LEN + 1]; // The (trimmed and uppercased) territory code, from the input. @@ -136,7 +136,7 @@ enum MapcodeError { ERR_INVALID_MAPCODE_FORMAT, // string not recognized as mapcode format ERR_INVALID_CHARACTER, // mapcode contains an invalid character ERR_BAD_ARGUMENTS, // an argument is invalid (e.g. NULL) - ERR_INVALID_ENDVOWELS, // mapcodes ends in UE or UU + ERR_INVALID_ENDVOWELS, // mapcode ends in UE or UU ERR_EXTENSION_INVALID_LENGTH, // precision extension too long, or empty ERR_EXTENSION_INVALID_CHARACTER, // bad precision extension character (e.g. Z) ERR_UNEXPECTED_DOT, // mapcode dot can not be in this position @@ -156,7 +156,7 @@ enum MapcodeError { ERR_MISSING_TERRITORY, // mapcode can not be decoded without territory ERR_EXTENSION_UNDECODABLE, // extension does not decode to valid coordinate ERR_MAPCODE_UNDECODABLE, // mapcode does not decode inside territory - ERR_BAD_COORDINATE, // latitude or longitude is NAN or infinite + ERR_BAD_COORDINATE, // latitude or longitude is NaN or infinite // all OK. @@ -178,9 +178,8 @@ enum MapcodeError { * make them represent the coordinate more accurately. * * Returns: - * Number of results stored in parameter results. Always >= 0 (0 if no encoding was possible or an error occurred). - * The results are stored as pairs (Mapcode, territory name) in: - * (results[0], results[1])...(results[(2 * N) - 2], results[(2 * N) - 1]) + * Number of results stored in `mapcodes->count`. Always >= 0 (0 if no encoding was possible or an error occurred). + * Each resulting string is stored in `mapcodes->mapcode[i]` and may include a territory ISO3166 code followed by a space when applicable. */ int encodeLatLonToMapcodes( @@ -193,12 +192,11 @@ int encodeLatLonToMapcodes( /** * Encode a latitude, longitude pair (in degrees) to a single Mapcode: the shortest possible for the given territory - * (which can be 0 for all territories). + * (pass TERRITORY_NONE or TERRITORY_UNKNOWN to consider all territories). * * Arguments: - * result - Returned Mapcode. The caller must not allocate or de-allocated this string. - * The resulting string MUST be allocated (and de-allocated) by the caller. - * The caller should allocate at least MAX_MAPCODE_RESULT_ASCII_LEN characters for the string. + * mapcode - Output buffer for the resulting Mapcode. The caller must allocate this buffer + * with capacity of at least MAX_MAPCODE_RESULT_ASCII_LEN characters. * lat - Latitude, in degrees. Range: -90..90. * lon - Longitude, in degrees. Range: -180..180. * territory - Territory (e.g. as obtained from getTerritoryCode), used as encoding context. @@ -224,9 +222,8 @@ int encodeLatLonToSingleMapcode( * (like Swift). * * Arguments: - * result - Returned Mapcode. The caller must not allocate or de-allocated this string. - * The resulting string MUST be allocated (and de-allocated) by the caller. - * The caller should allocate at least MAX_MAPCODE_RESULT_ASCII_LEN characters for the string. + * mapcode - Output buffer for the resulting Mapcode. The caller must allocate this buffer + * with capacity of at least MAX_MAPCODE_RESULT_ASCII_LEN characters. * lat - Latitude, in degrees. Range: -90..90. * lon - Longitude, in degrees. Range: -180..180. * territory - Territory (e.g. as obtained from getTerritoryCode), used as encoding context. @@ -251,7 +248,7 @@ int encodeLatLonToSelectedMapcode( /** - * Decode a utf8 or ascii Mapcode to a latitude, longitude pair (in degrees). + * Decode a UTF-8 or ASCII Mapcode to a latitude/longitude pair (in degrees). * * Arguments: * lat - Decoded latitude, in degrees. Range: -90..90. @@ -262,7 +259,7 @@ int encodeLatLonToSelectedMapcode( * mapcodeElements - If not NULL, filled with analysis of the string (unless an error was encountered). * * Returns: - * ERR_OK if encoding succeeded. + * ERR_OK if decoding succeeded. */ enum MapcodeError decodeMapcodeToLatLonUtf8( double* latDeg, @@ -273,18 +270,18 @@ enum MapcodeError decodeMapcodeToLatLonUtf8( /** - * Decode a utf16 Mapcode to a latitude, longitude pair (in degrees). + * Decode a UTF-16 Mapcode to a latitude/longitude pair (in degrees). * * Arguments: * lat - Decoded latitude, in degrees. Range: -90..90. * lon - Decoded longitude, in degrees. Range: -180..180. - * mapcodeElements - If not NULL, filled with analysis of the string (unless an error was encountered) - * utf8string - Mapcode to decode (ascii or utf8 string). + * utf16string - Mapcode to decode (UTF-16 string). * territory - Territory (e.g. as obtained from getTerritoryCode), used as decoding context. * Pass TERRITORY_NONE if not available. + * mapcodeElements - If not NULL, filled with analysis of the string (unless an error was encountered). * * Returns: - * ERR_OK if encoding succeeded. + * ERR_OK if decoding succeeded. */ enum MapcodeError decodeMapcodeToLatLonUtf16( double* latDeg, @@ -299,7 +296,7 @@ enum MapcodeError decodeMapcodeToLatLonUtf16( * the return value ERR_OK indicates the string has the Mapcode format, much like string comparison strcmp returns.) * * Arguments: - * utf8String/utf16String - Mapcode string to check, in UTF8 or UTF16 format. + * utf8String/utf16String - Mapcode string to check, in UTF-8 or UTF-16 format. * * Returns: * ERR_OK if the string has a correct Mapcode format, another ERR_XXX value if the string does @@ -318,8 +315,8 @@ enum MapcodeError compareWithMapcodeFormatUtf16(const UWORD* utf16String); * Convert an ISO3166 territory code to a territory. * * Arguments: - * territoryISO - String starting with ISO3166 code of territory (e.g. "USA" or "US-CA"). - * parentTerritoryCode - Parent territory, or TERRITORY_NONE if not available. + * territoryISO - String starting with ISO3166 code of territory (e.g. "USA" or "US-CA"). + * optionalTerritoryContext - Parent territory, or TERRITORY_NONE if not available. * * Returns: * Territory (> _TERRITORY_MIN) if succeeded, or TERRITORY_NONE if failed. @@ -333,9 +330,9 @@ enum Territory getTerritoryCode( * Convert a territory to a territory name. * * Arguments: - * territoryISO - String to territory ISO code name result. + * territoryISO - Output buffer for the territory ISO code name. * territory - Territory to get the name of. - * userShortName - Pass 0 for full name, 1 for short name (state codes may be ambiguous). + * useShortName - Pass 0 for full name, 1 for short name (state codes may be ambiguous). * * Returns: * Pointer to result. String will be empty if territory illegal. @@ -347,8 +344,8 @@ char* getTerritoryIsoName( /** - * Given a territory, return the territory itself it it was a country, or return its parent - * territory if it was a subdivision (e.g. a state). + * Given a territory, return the territory itself if it is a country, or return its parent + * territory if it is a subdivision (e.g. a state). * * Arguments: * territory - territory (either a country or a subdivision, e.g. a state). @@ -418,7 +415,7 @@ int multipleBordersNearby( * Returns territory names in English. There's always at least 1 alternative (with index 0). * * Arguments: - * territoryName - Target string, allocated by caller to be at least MAX_TERRITORY_FULLNAME_ASCII_LEN + 1 bytes. + * territoryName - Target string, allocated by caller to be at least MAX_TERRITORY_FULLNAME_UTF8_LEN + 1 bytes. * territory - Territory to get name for. * alternative - Which name to get, must be >= 0 (0 = default, 1 = first alternative, 2 = second etc.). * @@ -490,7 +487,7 @@ int getFullTerritoryNameInLocaleInAlphabetUtf8( /** - * This struct contains the returned alphabest for getAlphabetsForTerritory. The 'count' specifies + * This struct contains the returned alphabets for getAlphabetsForTerritory. The 'count' specifies * how many alphabets are listed in 'alphabet', range [1, MAX_ALPHABETS_PER_TERRITORY]. */ #define MAX_ALPHABETS_PER_TERRITORY 3 @@ -510,7 +507,7 @@ typedef struct { * * Returns: * A pointer to a TerritoryAlphabets structure, or NULL if the territory is invalid. - * (The pointer is owned by the library and should not be dealloacted by the caller.) + * (The pointer is owned by the library and should not be deallocated by the caller.) */ const TerritoryAlphabets* getAlphabetsForTerritory(enum Territory territory); @@ -525,7 +522,7 @@ const TerritoryAlphabets* getAlphabetsForTerritory(enum Territory territory); * alphabet - Alphabet to use. * * Returns: - * Encode UTF8 string (pointer to utf8String buffer), allocated and deallocated by the caller. + * Encoded UTF-8 string (pointer to utf8String buffer), allocated and deallocated by the caller. */ char* convertMapcodeToAlphabetUtf8(char* utf8String, const char* asciiString, enum Alphabet alphabet); @@ -540,7 +537,7 @@ char* convertMapcodeToAlphabetUtf8(char* utf8String, const char* asciiString, en * alphabet - Alphabet to use. * * Returns: - * Encode UTF16 string (pointer to utf16String buffer), allocated and deallocated by the caller. + * Encoded UTF-16 string (pointer to utf16String buffer), allocated and deallocated by the caller. */ UWORD* convertMapcodeToAlphabetUtf16(UWORD* utf16String, const char* asciiString, enum Alphabet alphabet); From 2634d1836b6ad1728cdfe55b3b9c0e989ea1ab8c Mon Sep 17 00:00:00 2001 From: Rijn Buve Date: Thu, 11 Sep 2025 10:17:26 +0200 Subject: [PATCH 13/31] Fix comments --- mapcodelib/mapcode_alphabets.h | 2 +- mapcodelib/mapcode_legacy.h | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/mapcodelib/mapcode_alphabets.h b/mapcodelib/mapcode_alphabets.h index 90bd4c8..7ffab2b 100644 --- a/mapcodelib/mapcode_alphabets.h +++ b/mapcodelib/mapcode_alphabets.h @@ -20,7 +20,7 @@ extern "C" { #endif /** - * Mapcodes are suppored in a variety of alphabets, using UTF16. The following + * Mapcodes are supported in a variety of alphabets, using UTF-16. The following * enum specifies the alphabets (or scripts, as they are also called). * The 'default' alphabet is Roman, which is always supported. Other * alphabets may not be supported by every application that accepts diff --git a/mapcodelib/mapcode_legacy.h b/mapcodelib/mapcode_legacy.h index 7b125a5..566a1a7 100644 --- a/mapcodelib/mapcode_legacy.h +++ b/mapcodelib/mapcode_legacy.h @@ -80,9 +80,9 @@ extern "C" { * Encode a latitude, longitude pair (in degrees) to a set of Mapcodes. Not thread-safe! * * Arguments: - * mapcodesAndTerritories - Results set of mapcodes and territories. + * mapcodesAndTerritories - Result set of mapcodes and territories. * The caller must pass an array of at least 2 * MAX_NR_OF_MAPCODE_RESULTS - * string points, which must NOT be allocated or de-allocated by the caller. + * string pointers, which must NOT be allocated or de-allocated by the caller. * The resulting strings are statically allocated by the library and will be overwritten * by the next call to this method! * lat - Latitude, in degrees. Range: -90..90. @@ -94,8 +94,8 @@ extern "C" { * make them represent the coordinate more accurately. * * Returns: - * Number of results stored in parameter results. Always >= 0 (0 if no encoding was possible or an error occurred). - * The results are stored as pairs (Mapcode, territory name) in: + * Number of results (N). Always >= 0 (0 if no encoding was possible or an error occurred). + * The results are stored as pairs (Mapcode, territory name) in mapcodesAndTerritories: * (results[0], results[1])...(results[(2 * N) - 2], results[(2 * N) - 1]) */ int encodeLatLonToMapcodes_Deprecated( // Warning: this method is deprecated and not thread-safe. @@ -110,11 +110,11 @@ int encodeLatLonToMapcodes_Deprecated( // Warning: this method is deprecated and * DEPRECATED OLD VARIANT, NOT THREAD-SAFE: * * Convert a territory to a territory name. - * Non-threadsafe routine which uses static storage, overwritten at each call. + * Non-thread-safe routine which uses static storage, overwritten at each call. * * Arguments: * territory - Territory to get the name of. - * userShortName - Pass 0 for full name, 1 for short name (state codes may be ambiguous). + * useShortName - Pass 0 for full name, 1 for short name (state codes may be ambiguous). * * Returns: * Pointer to result. String will be empty if territory illegal. @@ -148,7 +148,7 @@ char* convertToRoman(char* asciiString, int maxLength, const UWORD* utf16String) * alphabet - Alphabet to use. * * Returns: - * Encoded Unicode string, points at buffer from 'utf16String', allocated/deallocated by caller. + * Encoded Unicode string, points at buffer from 'utf16String', allocated and deallocated by the caller. */ UWORD* convertToAlphabet(UWORD* utf16String, int maxLength, const char* asciiString, enum Alphabet alphabet); From 47e0d3220b7868fca2bdcaa3f360f2ae707d03f2 Mon Sep 17 00:00:00 2001 From: Rijn Buve Date: Thu, 28 May 2026 08:16:57 +0200 Subject: [PATCH 14/31] Fix critical and major code review issues - Replace memcmp-based NaN/Inf detection with isnan()/isinf() from (portable, correct for all NaN bit patterns, and now checks latDeg for Inf too) - Fix off-by-one in encodeLatLonToSelectedMapcode: > should be >= for index bound - Remove always-false ASSERT after *s=0 in encodeExtension - Fix convertFromAbjad/convertToAbjad to null-check strchr return before arithmetic - Allow TERRITORY_NONE/TERRITORY_UNKNOWN in encodeLatLonToSingleMapcode per docs - Fix convertUtf16ToUtf8 to return start pointer instead of post-null end pointer - Explicitly initialize GLOBAL_MAKEISO_PTR (was accidentally correct via NULL) - Replace sprintf with snprintf in convertToRoman (mapcode_legacy.c) - Change UWORD from unsigned short int to uint16_t; add #include - Replace magic constant 128 with MAX_MAPCODE_RESULT_ASCII_LEN in encoderEngine - Add regression tests for all fixed bugs in testBugFixes() Co-Authored-By: Claude Sonnet 4.6 --- .gitignore | 1 + mapcodelib/mapcode_legacy.c | 14 +++---- mapcodelib/mapcoder.c | 31 +++++++-------- mapcodelib/mapcoder.h | 3 +- test/unittest.c | 75 +++++++++++++++++++++++++++++++++++++ 5 files changed, 99 insertions(+), 25 deletions(-) diff --git a/.gitignore b/.gitignore index 05788f6..63fdf13 100644 --- a/.gitignore +++ b/.gitignore @@ -132,3 +132,4 @@ Release # ----------------------------------------------------------------------------- nb-configuration.xml *.orig +.gitnexus diff --git a/mapcodelib/mapcode_legacy.c b/mapcodelib/mapcode_legacy.c index b897e8a..6587ece 100644 --- a/mapcodelib/mapcode_legacy.c +++ b/mapcodelib/mapcode_legacy.c @@ -23,7 +23,7 @@ */ static Mapcodes GLOBAL_RESULT; static char GLOBAL_MAKEISO_BUFFER[2 * (MAX_ISOCODE_LEN + 1)]; -static char* GLOBAL_MAKEISO_PTR; +static char* GLOBAL_MAKEISO_PTR = GLOBAL_MAKEISO_BUFFER + (MAX_ISOCODE_LEN + 1); int encodeLatLonToMapcodes_Deprecated( @@ -97,12 +97,12 @@ char* convertToRoman(char* asciiBuffer, int maxLength, const UWORD* unicodeBuffe } if (!err) { char romanized[MAX_MAPCODE_RESULT_LEN]; - sprintf(romanized, "%s%s%s%s%s", - mapcodeElements.territoryISO, - *mapcodeElements.territoryISO ? " " : "", - mapcodeElements.properMapcode, - *mapcodeElements.precisionExtension ? "-" : "", - mapcodeElements.precisionExtension); + snprintf(romanized, sizeof(romanized), "%s%s%s%s%s", + mapcodeElements.territoryISO, + *mapcodeElements.territoryISO ? " " : "", + mapcodeElements.properMapcode, + *mapcodeElements.precisionExtension ? "-" : "", + mapcodeElements.precisionExtension); if ((int)strlen(romanized) < maxLength) { strcpy(asciiBuffer, romanized); } diff --git a/mapcodelib/mapcoder.c b/mapcodelib/mapcoder.c index a495957..e7b0a4d 100644 --- a/mapcodelib/mapcoder.c +++ b/mapcodelib/mapcoder.c @@ -534,17 +534,11 @@ static Point convertFractionsToDegrees(const Point* p) { } -static const unsigned char DOUBLE_NAN[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F}; // NAN (Not a Number) -static const unsigned char DOUBLE_INF[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x7F}; // +Infinity -static const unsigned char DOUBLE_MIN_INF[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF}; // -Infinity - static enum MapcodeError convertCoordsToMicrosAndFractions(Point32* coord32, int* fracLat, int* fracLon, double latDeg, double lonDeg) { double frac; ASSERT(coord32); - if (memcmp(&lonDeg, DOUBLE_NAN, 8) == 0 || memcmp(&lonDeg, DOUBLE_INF, 8) == 0 || - memcmp(&lonDeg, DOUBLE_MIN_INF, 8) == 0 || - memcmp(&latDeg, DOUBLE_NAN, 8) == 0) { + if (isnan(lonDeg) || isinf(lonDeg) || isnan(latDeg) || isinf(latDeg)) { return ERR_BAD_COORDINATE; } if (latDeg < -90) { @@ -1081,7 +1075,6 @@ static void encodeExtension(char* result, const int extrax4, const int extray, c valy -= factory * gy; // for next iteration } *s = 0; // terminate the result - ASSERT((int) strlen(s) == extraDigits); } } @@ -1487,7 +1480,7 @@ static void encoderEngine(const enum Territory ccode, const EncodeRec* enc, cons /////////////////////////////////////////////////////////// { int i; - char result[128]; + char result[MAX_MAPCODE_RESULT_ASCII_LEN]; int result_counter = 0; *result = 0; @@ -1736,7 +1729,7 @@ static int decodeBase31(const char* code) { static void decodeTriple(const char* result, int* difx, int* dify) { // decode the first character const int c1 = decodeChar(*result++); - ASSERT(result); + ASSERT(result - 1); ASSERT(difx); ASSERT(dify); if (c1 < 24) { @@ -2125,7 +2118,10 @@ static unsigned char getRomanVersionOf(UWORD w) { static void convertFromAbjad(char* s) { int len, dot, form, c; char* postfix = strchr(s, '-'); - dot = (int)(strchr(s, '.') - s); + { + const char* dotptr = strchr(s, '.'); + dot = dotptr ? (int)(dotptr - s) : -1; + } if (dot < 2 || dot > 5) { return; } @@ -3020,7 +3016,10 @@ static char* convertToAbjad(char* targetAsciiString, const char* sourceAsciiStri unpackIfAllDigits(targetAsciiString); len = (int)strlen(targetAsciiString); - dot = (int)(strchr(targetAsciiString, '.') - targetAsciiString); + { + const char* dotptr = strchr(targetAsciiString, '.'); + dot = dotptr ? (int)(dotptr - targetAsciiString) : -1; + } form = dot * 10 + (len - dot - 1); @@ -3297,6 +3296,7 @@ UWORD* convertToAlphabet(UWORD* utf16String, int maxLength, const char* asciiStr * Convert a zero-terminated UTF16 to a UTF8 string */ char* convertUtf16ToUtf8(char* utf8, const UWORD* utf16) { + char* start = utf8; ASSERT(utf16); ASSERT(utf8); while (*utf16) { @@ -3315,7 +3315,7 @@ char* convertUtf16ToUtf8(char* utf8, const UWORD* utf16) { } } *utf8 = 0; - return utf8; + return start; } // Caller must make sure utf8String can hold at least MAX_MAPCODE_RESULT_LEN characters (including 0-terminator). @@ -3585,9 +3585,6 @@ encodeLatLonToSingleMapcode(char* mapcode, double latDeg, double lonDeg, enum Te if (extraDigits > MAX_PRECISION_DIGITS) { extraDigits = MAX_PRECISION_DIGITS; } - if (territory <= TERRITORY_UNKNOWN) { - return 0; - } ret = encodeLatLonToMapcodes_internal(&rlocal, latDeg, lonDeg, territory, 1, DEBUG_STOP_AT, extraDigits); *mapcode = 0; if (ret <= 0) { @@ -3608,7 +3605,7 @@ encodeLatLonToSelectedMapcode(char* mapcode, double latDeg, double lonDeg, enum int nrOfResults = 0; nrOfResults = encodeLatLonToMapcodes(&mapcodes, latDeg, lonDeg, territory, extraDigits); ASSERT(nrOfResults == mapcodes.count); - if ((nrOfResults <= 0) || (indexOfSelected < 0) || (indexOfSelected > nrOfResults)) { + if ((nrOfResults <= 0) || (indexOfSelected < 0) || (indexOfSelected >= nrOfResults)) { return 0; } strcpy(mapcode, mapcodes.mapcode[indexOfSelected]); diff --git a/mapcodelib/mapcoder.h b/mapcodelib/mapcoder.h index 63e68b1..48d41f8 100644 --- a/mapcodelib/mapcoder.h +++ b/mapcodelib/mapcoder.h @@ -20,6 +20,7 @@ extern "C" { #endif +#include #include "mapcode_territories.h" #include "mapcode_alphabets.h" @@ -58,7 +59,7 @@ extern "C" { #endif #define MAPCODE_C_VERSION "2.5.6" -#define UWORD unsigned short int // 2-byte unsigned integer. +#define UWORD uint16_t // 2-byte unsigned integer. #define MAX_NR_OF_MAPCODE_RESULTS 22 // Max. number of results ever returned by encoder (e.g. for 26.904899, 95.138515). diff --git a/test/unittest.c b/test/unittest.c index 7d88cbf..b5b9f4f 100644 --- a/test/unittest.c +++ b/test/unittest.c @@ -2222,6 +2222,78 @@ static int testAlphabetPerTerritory(void) { } +static int testBugFixes(void) { + int nrTests = 0; + + // Issue 1: lat=+Inf or lat=-Inf must return ERR_BAD_COORDINATE (0 results), same as lon=Inf + { + Mapcodes mapcodes; + double pos_inf = 1.0 / 0.0; + double neg_inf = -1.0 / 0.0; + int n; + + n = encodeLatLonToMapcodes(&mapcodes, pos_inf, 0.0, TERRITORY_NONE, 0); + ++nrTests; + if (n != 0) { + foundError(); + printf("*** ERROR *** encodeLatLonToMapcodes(lat=+Inf) should return 0, got %d\n", n); + } + + n = encodeLatLonToMapcodes(&mapcodes, neg_inf, 0.0, TERRITORY_NONE, 0); + ++nrTests; + if (n != 0) { + foundError(); + printf("*** ERROR *** encodeLatLonToMapcodes(lat=-Inf) should return 0, got %d\n", n); + } + } + + // Issue 2: indexOfSelected == nrOfResults is out-of-bounds and must return 0 + { + char mapcode[MAX_MAPCODE_RESULT_ASCII_LEN]; + // lat=52.158993, lon=4.492346 yields 4 results (verified in testSelectedEncodes) + int n = encodeLatLonToSelectedMapcode(mapcode, 52.158993, 4.492346, TERRITORY_NONE, 0, 4); + ++nrTests; + if (n != 0) { + foundError(); + printf("*** ERROR *** encodeLatLonToSelectedMapcode with indexOfSelected==nrOfResults should return 0, got %d\n", n); + } + } + + // Issue 7: encodeLatLonToSingleMapcode must accept TERRITORY_NONE and TERRITORY_UNKNOWN + { + char mapcode[MAX_MAPCODE_RESULT_ASCII_LEN]; + int n; + + n = encodeLatLonToSingleMapcode(mapcode, 52.3, 4.9, TERRITORY_UNKNOWN, 0); + ++nrTests; + if (n <= 0) { + foundError(); + printf("*** ERROR *** encodeLatLonToSingleMapcode(TERRITORY_UNKNOWN) should succeed, got %d\n", n); + } + + n = encodeLatLonToSingleMapcode(mapcode, 52.3, 4.9, TERRITORY_NONE, 0); + ++nrTests; + if (n <= 0) { + foundError(); + printf("*** ERROR *** encodeLatLonToSingleMapcode(TERRITORY_NONE) should succeed, got %d\n", n); + } + } + + // Issue 9: convertMapcodeToAlphabetUtf8 must return the start of the output buffer + { + char utf8[MAX_MAPCODE_RESULT_UTF8_LEN + 1]; + char *result = convertMapcodeToAlphabetUtf8(utf8, "NLD 49.4V", ALPHABET_ROMAN); + ++nrTests; + if (result != utf8) { + foundError(); + printf("*** ERROR *** convertMapcodeToAlphabetUtf8 must return start of buffer, got wrong pointer\n"); + } + } + + return nrTests; +} + + int main(const int argc, const char **argv) { int nrTests = 0; @@ -2281,6 +2353,9 @@ int main(const int argc, const char **argv) { printf("-----------------------------------------------------------\nRe-encode tests\n"); nrTests += testReEncode(); + printf("-----------------------------------------------------------\nBug-fix regression tests\n"); + nrTests += testBugFixes(); + printf("-----------------------------------------------------------\n"); printf("Done.\nExecuted %d tests, found %d errors\n", nrTests, nrErrors); if (nrErrors > 0) { From ea028ffd6f2fb109e1c55db592ea51a5a5a97106 Mon Sep 17 00:00:00 2001 From: Rijn Buve Date: Thu, 28 May 2026 08:48:08 +0200 Subject: [PATCH 15/31] updagted version --- mapcodelib/mapcoder.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapcodelib/mapcoder.h b/mapcodelib/mapcoder.h index 48d41f8..918360d 100644 --- a/mapcodelib/mapcoder.h +++ b/mapcodelib/mapcoder.h @@ -58,7 +58,7 @@ extern "C" { #define MAPCODE_SUPPORT_LANGUAGE_UK #endif -#define MAPCODE_C_VERSION "2.5.6" +#define MAPCODE_C_VERSION "2.5.7" #define UWORD uint16_t // 2-byte unsigned integer. #define MAX_NR_OF_MAPCODE_RESULTS 22 // Max. number of results ever returned by encoder (e.g. for 26.904899, 95.138515). From a21ef29759e110e6134ed7953c409cc7a89c9316 Mon Sep 17 00:00:00 2001 From: Rijn Buve Date: Thu, 28 May 2026 08:16:57 +0200 Subject: [PATCH 16/31] Fix critical and major code review issues - Replace memcmp-based NaN/Inf detection with isnan()/isinf() from (portable, correct for all NaN bit patterns, and now checks latDeg for Inf too) - Fix off-by-one in encodeLatLonToSelectedMapcode: > should be >= for index bound - Remove always-false ASSERT after *s=0 in encodeExtension - Fix convertFromAbjad/convertToAbjad to null-check strchr return before arithmetic - Allow TERRITORY_NONE/TERRITORY_UNKNOWN in encodeLatLonToSingleMapcode per docs - Fix convertUtf16ToUtf8 to return start pointer instead of post-null end pointer - Explicitly initialize GLOBAL_MAKEISO_PTR (was accidentally correct via NULL) - Replace sprintf with snprintf in convertToRoman (mapcode_legacy.c) - Change UWORD from unsigned short int to uint16_t; add #include - Replace magic constant 128 with MAX_MAPCODE_RESULT_ASCII_LEN in encoderEngine - Add regression tests for all fixed bugs in testBugFixes() Co-Authored-By: Claude Sonnet 4.6 --- .gitignore | 1 + mapcodelib/mapcode_legacy.c | 14 +++---- mapcodelib/mapcoder.c | 31 +++++++-------- mapcodelib/mapcoder.h | 3 +- test/unittest.c | 75 +++++++++++++++++++++++++++++++++++++ 5 files changed, 99 insertions(+), 25 deletions(-) diff --git a/.gitignore b/.gitignore index 05788f6..63fdf13 100644 --- a/.gitignore +++ b/.gitignore @@ -132,3 +132,4 @@ Release # ----------------------------------------------------------------------------- nb-configuration.xml *.orig +.gitnexus diff --git a/mapcodelib/mapcode_legacy.c b/mapcodelib/mapcode_legacy.c index b897e8a..6587ece 100644 --- a/mapcodelib/mapcode_legacy.c +++ b/mapcodelib/mapcode_legacy.c @@ -23,7 +23,7 @@ */ static Mapcodes GLOBAL_RESULT; static char GLOBAL_MAKEISO_BUFFER[2 * (MAX_ISOCODE_LEN + 1)]; -static char* GLOBAL_MAKEISO_PTR; +static char* GLOBAL_MAKEISO_PTR = GLOBAL_MAKEISO_BUFFER + (MAX_ISOCODE_LEN + 1); int encodeLatLonToMapcodes_Deprecated( @@ -97,12 +97,12 @@ char* convertToRoman(char* asciiBuffer, int maxLength, const UWORD* unicodeBuffe } if (!err) { char romanized[MAX_MAPCODE_RESULT_LEN]; - sprintf(romanized, "%s%s%s%s%s", - mapcodeElements.territoryISO, - *mapcodeElements.territoryISO ? " " : "", - mapcodeElements.properMapcode, - *mapcodeElements.precisionExtension ? "-" : "", - mapcodeElements.precisionExtension); + snprintf(romanized, sizeof(romanized), "%s%s%s%s%s", + mapcodeElements.territoryISO, + *mapcodeElements.territoryISO ? " " : "", + mapcodeElements.properMapcode, + *mapcodeElements.precisionExtension ? "-" : "", + mapcodeElements.precisionExtension); if ((int)strlen(romanized) < maxLength) { strcpy(asciiBuffer, romanized); } diff --git a/mapcodelib/mapcoder.c b/mapcodelib/mapcoder.c index a495957..e7b0a4d 100644 --- a/mapcodelib/mapcoder.c +++ b/mapcodelib/mapcoder.c @@ -534,17 +534,11 @@ static Point convertFractionsToDegrees(const Point* p) { } -static const unsigned char DOUBLE_NAN[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F}; // NAN (Not a Number) -static const unsigned char DOUBLE_INF[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x7F}; // +Infinity -static const unsigned char DOUBLE_MIN_INF[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xFF}; // -Infinity - static enum MapcodeError convertCoordsToMicrosAndFractions(Point32* coord32, int* fracLat, int* fracLon, double latDeg, double lonDeg) { double frac; ASSERT(coord32); - if (memcmp(&lonDeg, DOUBLE_NAN, 8) == 0 || memcmp(&lonDeg, DOUBLE_INF, 8) == 0 || - memcmp(&lonDeg, DOUBLE_MIN_INF, 8) == 0 || - memcmp(&latDeg, DOUBLE_NAN, 8) == 0) { + if (isnan(lonDeg) || isinf(lonDeg) || isnan(latDeg) || isinf(latDeg)) { return ERR_BAD_COORDINATE; } if (latDeg < -90) { @@ -1081,7 +1075,6 @@ static void encodeExtension(char* result, const int extrax4, const int extray, c valy -= factory * gy; // for next iteration } *s = 0; // terminate the result - ASSERT((int) strlen(s) == extraDigits); } } @@ -1487,7 +1480,7 @@ static void encoderEngine(const enum Territory ccode, const EncodeRec* enc, cons /////////////////////////////////////////////////////////// { int i; - char result[128]; + char result[MAX_MAPCODE_RESULT_ASCII_LEN]; int result_counter = 0; *result = 0; @@ -1736,7 +1729,7 @@ static int decodeBase31(const char* code) { static void decodeTriple(const char* result, int* difx, int* dify) { // decode the first character const int c1 = decodeChar(*result++); - ASSERT(result); + ASSERT(result - 1); ASSERT(difx); ASSERT(dify); if (c1 < 24) { @@ -2125,7 +2118,10 @@ static unsigned char getRomanVersionOf(UWORD w) { static void convertFromAbjad(char* s) { int len, dot, form, c; char* postfix = strchr(s, '-'); - dot = (int)(strchr(s, '.') - s); + { + const char* dotptr = strchr(s, '.'); + dot = dotptr ? (int)(dotptr - s) : -1; + } if (dot < 2 || dot > 5) { return; } @@ -3020,7 +3016,10 @@ static char* convertToAbjad(char* targetAsciiString, const char* sourceAsciiStri unpackIfAllDigits(targetAsciiString); len = (int)strlen(targetAsciiString); - dot = (int)(strchr(targetAsciiString, '.') - targetAsciiString); + { + const char* dotptr = strchr(targetAsciiString, '.'); + dot = dotptr ? (int)(dotptr - targetAsciiString) : -1; + } form = dot * 10 + (len - dot - 1); @@ -3297,6 +3296,7 @@ UWORD* convertToAlphabet(UWORD* utf16String, int maxLength, const char* asciiStr * Convert a zero-terminated UTF16 to a UTF8 string */ char* convertUtf16ToUtf8(char* utf8, const UWORD* utf16) { + char* start = utf8; ASSERT(utf16); ASSERT(utf8); while (*utf16) { @@ -3315,7 +3315,7 @@ char* convertUtf16ToUtf8(char* utf8, const UWORD* utf16) { } } *utf8 = 0; - return utf8; + return start; } // Caller must make sure utf8String can hold at least MAX_MAPCODE_RESULT_LEN characters (including 0-terminator). @@ -3585,9 +3585,6 @@ encodeLatLonToSingleMapcode(char* mapcode, double latDeg, double lonDeg, enum Te if (extraDigits > MAX_PRECISION_DIGITS) { extraDigits = MAX_PRECISION_DIGITS; } - if (territory <= TERRITORY_UNKNOWN) { - return 0; - } ret = encodeLatLonToMapcodes_internal(&rlocal, latDeg, lonDeg, territory, 1, DEBUG_STOP_AT, extraDigits); *mapcode = 0; if (ret <= 0) { @@ -3608,7 +3605,7 @@ encodeLatLonToSelectedMapcode(char* mapcode, double latDeg, double lonDeg, enum int nrOfResults = 0; nrOfResults = encodeLatLonToMapcodes(&mapcodes, latDeg, lonDeg, territory, extraDigits); ASSERT(nrOfResults == mapcodes.count); - if ((nrOfResults <= 0) || (indexOfSelected < 0) || (indexOfSelected > nrOfResults)) { + if ((nrOfResults <= 0) || (indexOfSelected < 0) || (indexOfSelected >= nrOfResults)) { return 0; } strcpy(mapcode, mapcodes.mapcode[indexOfSelected]); diff --git a/mapcodelib/mapcoder.h b/mapcodelib/mapcoder.h index 63e68b1..48d41f8 100644 --- a/mapcodelib/mapcoder.h +++ b/mapcodelib/mapcoder.h @@ -20,6 +20,7 @@ extern "C" { #endif +#include #include "mapcode_territories.h" #include "mapcode_alphabets.h" @@ -58,7 +59,7 @@ extern "C" { #endif #define MAPCODE_C_VERSION "2.5.6" -#define UWORD unsigned short int // 2-byte unsigned integer. +#define UWORD uint16_t // 2-byte unsigned integer. #define MAX_NR_OF_MAPCODE_RESULTS 22 // Max. number of results ever returned by encoder (e.g. for 26.904899, 95.138515). diff --git a/test/unittest.c b/test/unittest.c index 7d88cbf..b5b9f4f 100644 --- a/test/unittest.c +++ b/test/unittest.c @@ -2222,6 +2222,78 @@ static int testAlphabetPerTerritory(void) { } +static int testBugFixes(void) { + int nrTests = 0; + + // Issue 1: lat=+Inf or lat=-Inf must return ERR_BAD_COORDINATE (0 results), same as lon=Inf + { + Mapcodes mapcodes; + double pos_inf = 1.0 / 0.0; + double neg_inf = -1.0 / 0.0; + int n; + + n = encodeLatLonToMapcodes(&mapcodes, pos_inf, 0.0, TERRITORY_NONE, 0); + ++nrTests; + if (n != 0) { + foundError(); + printf("*** ERROR *** encodeLatLonToMapcodes(lat=+Inf) should return 0, got %d\n", n); + } + + n = encodeLatLonToMapcodes(&mapcodes, neg_inf, 0.0, TERRITORY_NONE, 0); + ++nrTests; + if (n != 0) { + foundError(); + printf("*** ERROR *** encodeLatLonToMapcodes(lat=-Inf) should return 0, got %d\n", n); + } + } + + // Issue 2: indexOfSelected == nrOfResults is out-of-bounds and must return 0 + { + char mapcode[MAX_MAPCODE_RESULT_ASCII_LEN]; + // lat=52.158993, lon=4.492346 yields 4 results (verified in testSelectedEncodes) + int n = encodeLatLonToSelectedMapcode(mapcode, 52.158993, 4.492346, TERRITORY_NONE, 0, 4); + ++nrTests; + if (n != 0) { + foundError(); + printf("*** ERROR *** encodeLatLonToSelectedMapcode with indexOfSelected==nrOfResults should return 0, got %d\n", n); + } + } + + // Issue 7: encodeLatLonToSingleMapcode must accept TERRITORY_NONE and TERRITORY_UNKNOWN + { + char mapcode[MAX_MAPCODE_RESULT_ASCII_LEN]; + int n; + + n = encodeLatLonToSingleMapcode(mapcode, 52.3, 4.9, TERRITORY_UNKNOWN, 0); + ++nrTests; + if (n <= 0) { + foundError(); + printf("*** ERROR *** encodeLatLonToSingleMapcode(TERRITORY_UNKNOWN) should succeed, got %d\n", n); + } + + n = encodeLatLonToSingleMapcode(mapcode, 52.3, 4.9, TERRITORY_NONE, 0); + ++nrTests; + if (n <= 0) { + foundError(); + printf("*** ERROR *** encodeLatLonToSingleMapcode(TERRITORY_NONE) should succeed, got %d\n", n); + } + } + + // Issue 9: convertMapcodeToAlphabetUtf8 must return the start of the output buffer + { + char utf8[MAX_MAPCODE_RESULT_UTF8_LEN + 1]; + char *result = convertMapcodeToAlphabetUtf8(utf8, "NLD 49.4V", ALPHABET_ROMAN); + ++nrTests; + if (result != utf8) { + foundError(); + printf("*** ERROR *** convertMapcodeToAlphabetUtf8 must return start of buffer, got wrong pointer\n"); + } + } + + return nrTests; +} + + int main(const int argc, const char **argv) { int nrTests = 0; @@ -2281,6 +2353,9 @@ int main(const int argc, const char **argv) { printf("-----------------------------------------------------------\nRe-encode tests\n"); nrTests += testReEncode(); + printf("-----------------------------------------------------------\nBug-fix regression tests\n"); + nrTests += testBugFixes(); + printf("-----------------------------------------------------------\n"); printf("Done.\nExecuted %d tests, found %d errors\n", nrTests, nrErrors); if (nrErrors > 0) { From 794e31f71ec6ec0ee9e2ea024b1dc60fe4b12e84 Mon Sep 17 00:00:00 2001 From: Rijn Buve Date: Thu, 28 May 2026 08:48:08 +0200 Subject: [PATCH 17/31] updagted version --- mapcodelib/mapcoder.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapcodelib/mapcoder.h b/mapcodelib/mapcoder.h index 48d41f8..918360d 100644 --- a/mapcodelib/mapcoder.h +++ b/mapcodelib/mapcoder.h @@ -58,7 +58,7 @@ extern "C" { #define MAPCODE_SUPPORT_LANGUAGE_UK #endif -#define MAPCODE_C_VERSION "2.5.6" +#define MAPCODE_C_VERSION "2.5.7" #define UWORD uint16_t // 2-byte unsigned integer. #define MAX_NR_OF_MAPCODE_RESULTS 22 // Max. number of results ever returned by encoder (e.g. for 26.904899, 95.138515). From 740bfec8a9f8838efef09bc5ca85f4f3bbe6961f Mon Sep 17 00:00:00 2001 From: Rijn Buve Date: Thu, 28 May 2026 09:08:32 +0200 Subject: [PATCH 18/31] docs: design spec for mapcodelib speed optimization Two-phase plan (A: local hot-path cleanups; B: precomputed companion tables) targeting 20-50% wall-time reduction on `time ./unittest` at -O3, preserving bit-exact output, strict portable C99/C11, and no runtime/heap growth. Co-Authored-By: Claude Sonnet 4.6 --- .../2026-05-28-mapcodelib-optimize-design.md | 194 ++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 docs/superpowers/specs/2026-05-28-mapcodelib-optimize-design.md diff --git a/docs/superpowers/specs/2026-05-28-mapcodelib-optimize-design.md b/docs/superpowers/specs/2026-05-28-mapcodelib-optimize-design.md new file mode 100644 index 0000000..f351078 --- /dev/null +++ b/docs/superpowers/specs/2026-05-28-mapcodelib-optimize-design.md @@ -0,0 +1,194 @@ +# mapcodelib speed optimization — design spec + +- **Date:** 2026-05-28 +- **Branch:** `feat/optimize` (off `fix/bugfix`) +- **Author:** Rijn Buve (with Claude) + +## 1. Scope & goals + +Achieve a substantial speedup on the `mapcoder.c` encode and decode hot paths, measured by `time ./unittest` in `test/` (real wall-clock against the existing unit test suite, which already loops over millions of points). + +**Target:** 20–50% wall-time reduction at `-O3`. + +### Hard guardrails + +- **Bit-exact output.** All existing unit tests must pass unchanged. No test files modified. +- **Strict portable C99/C11.** No `__builtin_*`, no SIMD intrinsics, no compiler-specific attributes. +- **No runtime / heap growth.** Per-call stack and heap usage must not increase. Public ABI (struct sizes in `mapcoder.h`) unchanged. +- **Small static-table growth OK.** Precomputed companion tables in the order of tens of KB are acceptable (negligible against existing data tables in `internal_data.h`). + +### Out of scope + +- Public API changes. +- Refactoring unrelated to performance. +- New features. +- Build-system changes (CMake, scripts) beyond what's strictly needed to compile. +- Approach C (loop restructuring, DFA decode parser) — deferred to a future branch if A+B underdeliver. + +## 2. Measurement methodology + +1. Build with `-O3` exactly as `test/run_normal.sh` does: + ``` + cd mapcodelib && gcc -O3 -c mapcoder.c + cd ../test && gcc -O3 unittest.c -lm -lpthread -o unittest ../mapcodelib/mapcoder.o + ``` +2. Run `time ./unittest` three times back-to-back; record the best `user` time. +3. Repeat after each commit that touches `mapcoder.c`. + +### Baseline + +Capture the baseline on the **first commit** of `feat/optimize` (before any code change) so we have a clean reference number. + +### Per-commit reporting + +Each optimization commit's message includes: + +- `time` output before vs. after (best of 3). +- Cumulative speedup vs. the baseline. +- One-line description of the change. + +### Test integrity + +Every commit must end with `unittest` printing `Unit tests passed` and zero errors. Any commit that regresses tests is rolled back before continuing. + +### Noise caveat + +The unit test does correctness work (asserts, sprintf, comparisons) in addition to encode/decode, so a 30% speedup of encode/decode core will appear as a smaller wall-time delta (perhaps 10–20%). That is expected; "significant" is judged on the total wall-clock number since that is the measurement method chosen. + +## 3. Approach A — Local hot-path cleanups + +Discrete, low-risk edits in `mapcoder.c`. Each is independently revertable and individually testable. + +### A1. Cache `flags` per iteration in encode/decode loops + +In `encoderEngine` (`mapcoder.c:1460`) and `decoderEngine` (`mapcoder.c:2651`), the loop body invokes `IS_NAMELESS(i)`, `IS_RESTRICTED(i)`, `IS_SPECIAL_SHAPE(i)`, `REC_TYPE(i)`, `coDex(i)`, `SMART_DIV(i)` on the same `i`. Each macro re-reads `TERRITORY_BOUNDARIES[i].flags`. + +Replace with a single `const int flags = TERRITORY_BOUNDARIES[i].flags;` at the top of the loop body, plus local extracted variables where used more than once. + +Same pattern in `firstNamelessRecord` (`mapcoder.c:822`) and `countNamelessRecords` (`mapcoder.c:835`). + +### A2. Reorder checks in `fitsInsideBoundaries` + +`mapcoder.c:602` checks lat-min, lat-max, lon-range. For mapcode territory boundaries (most are narrow longitudinally relative to the full earth lon range), test longitude band first to maximise early-out. Same logic, same correctness, fewer comparisons on average. + +### A3. Length-tracked result assembly in `encoderEngine` + +Around `mapcoder.c:1520` the per-result code uses `strcpy` + `strcat` patterns. `strcat` re-scans the destination from the start each time. Replace with `memcpy` using known lengths. + +### A4. Speed up `encodeBase31` + +`mapcoder.c:1083` emits a base-31 representation by repeated `%31` / `/31`. The compiler turns `/31` into a magic-multiply, but the loop is small. A tight unroll (or const lookup for the common small `nrchars` cases) can help. + +### A5. Early-exit in `repackIfAllDigits` / `unpackIfAllDigits` + +`mapcoder.c:892`, `:933`. If the input clearly fails the "all digits" precondition (e.g. first non-digit position), bail before any string mutation. The existing code already does some of this; tighten the check. + +### Risk per change + +Very low; each one is mechanically equivalent. Run full unit tests after each commit. + +### Expected gain from A + +~10–20% wall-time. + +## 4. Approach B — Precomputed companion tables + +Core insight: `encoderEngine`/`decoderEngine` loop over a range of records and re-derive metadata (`codex`, `recType`, `isNameless`, `isSpecialShape`, `smartDiv`) from `flags` on every iteration. Approach A caches `flags` per-iteration, but across many iterations we still recompute the same masks/shifts on the same record data. Precompute that metadata into a compact companion table once at library init, and have the hot loops read directly from it. + +### B1. Companion arrays sized to `TERRITORY_BOUNDARIES` + +Add to `mapcoder.c` at file scope (not exported): + +```c +// One byte per record. Initialized once via initCompanionTables(). +static unsigned char RECORD_CODEX[MAPCODE_BOUNDARY_MAX + 1]; // flags & 31 +static unsigned char RECORD_REC_TYPE[MAPCODE_BOUNDARY_MAX + 1]; // (flags >> 7) & 3 +static unsigned char RECORD_KIND[MAPCODE_BOUNDARY_MAX + 1]; // bit0=nameless, bit1=restricted, + // bit2=specialShape, bit3=hasHeaderLetter +static unsigned char RECORD_HEADER_LETTER[MAPCODE_BOUNDARY_MAX + 1]; // ENCODE_CHARS[(flags >> 11) & 31] +static unsigned short RECORD_SMART_DIV[MAPCODE_BOUNDARY_MAX + 1]; // flags >> 16 +``` + +Per-record overhead: 6 bytes. With ~12,000 records, total ≈ 72 KB of static `.bss`/`.data`. Well within "small additions OK". + +### B2. Per-territory derived metadata + +```c +static int TERRITORY_FIRST_NAMELESS[_TERRITORY_MAX + 1]; // -1 if none +static int TERRITORY_NAMELESS_COUNT[_TERRITORY_MAX + 1]; // 0 if none +``` + +Eliminates linear scans in `firstNamelessRecord` (`mapcoder.c:822`) and `countNamelessRecords` (`mapcoder.c:835`), turning O(records-per-territory) into O(1). With ~241 territories, ~2 KB extra. + +### B3. One-time initialization + +A `static int companion_initialized = 0;` flag and an `initCompanionTables()` function called at the top of every public entry point that uses the data (`encodeLatLonToMapcodes_internal`, `decoderEngine`, etc.). The init computes everything from `TERRITORY_BOUNDARIES` and the flag-extraction macros — guaranteeing the precomputed values are *definitionally* the same as the macros. + +**Thread safety.** The public API is not specified as thread-safe for first call; the existing code uses pthread only in tests. Init is idempotent (writes deterministic values), so even under a first-call race the worst case is duplicate work — never wrong values. This assumption is documented inline. + +### B4. Replace macro call sites in hot loops only + +`IS_NAMELESS(m)` and the other macros stay defined (used in cold paths, debug asserts, and the init function itself). In `encoderEngine`/`decoderEngine` inner loops, switch to direct table reads: + +```c +const unsigned char kind = RECORD_KIND[i]; +const int codex = RECORD_CODEX[i]; +// ... +``` + +This decouples hot-loop performance from the bit-pack layout of `flags`. + +### B5. Debug-build sanity check + +In `#ifdef DEBUG` builds, `initCompanionTables` also asserts that the precomputed values match the macro-derived values for every record. Free correctness guarantee during development; zero cost in `-O3` release. + +### Risk + +Medium-low. Companion tables are derived directly from the same macros they replace, so by construction they hold identical values. The debug assertions catch any drift if someone later modifies the flag layout. + +### Interaction with A1 + +A1 caches `flags` locally in the very same hot loops that B4 later switches to direct companion-table reads. After B4 lands, the local `flags` cache in those specific loop bodies becomes unused and must be removed as part of that commit. A1 remains useful in any cold-path call site that does not switch to companion tables. + +### Expected combined gain from A+B + +~20–35% wall-time. + +## 5. Workflow + +One commit per checkpoint, linear progression: + +| # | Commit | Purpose | +|---|--------|---------| +| 1 | `chore: branch baseline` | Pin the baseline. Run `time ./unittest` 3× and record best `user` time in commit message. | +| 2 | `perf: A1 — cache flags per loop iteration` | Smallest, safest first. Re-run timing. | +| 3 | `perf: A2 — reorder fitsInsideBoundaries checks` | | +| 4 | `perf: A3 — length-tracked result assembly` | | +| 5 | `perf: A4 — tighten encodeBase31` | | +| 6 | `perf: A5 — early-exit in repack/unpack` | | +| 7 | `perf: B1+B2+B3 — companion tables init` | Tables defined and populated, but not yet read in hot path. Verifies init correctness in isolation. | +| 8 | `perf: B4 — hot loops read from companion tables` | The main payoff commit. | +| 9 | `perf: B5 — debug-build sanity check` | Optional polish. | + +Each commit ends with: + +- Full unit tests green (`Unit tests passed`, 0 errors). +- `time ./unittest` (best of 3) noted in the commit body, plus cumulative speedup vs. commit #1. + +## 6. Risks & mitigations + +| Risk | Mitigation | +|------|------------| +| A change breaks bit-exact behaviour | Run full test suite after every commit; revert if red. | +| Companion table values drift from `flags` semantics | B5 debug-build assertion verifies equivalence at init. | +| Thread-safety regression on first call | `initCompanionTables` is idempotent (deterministic writes). Documented. Safe under existing usage patterns. | +| Gains too small to justify | Stop after A and reassess; A alone should be ≥10% and is the lowest-risk subset. | +| Test wall-time too noisy to read signal | Use best-of-3 `user` time (not `real`), build with `-O3`, run on a quiet machine. | + +## 7. Success criteria + +- **Mandatory:** all unit tests pass after every commit. +- **Mandatory:** binary size growth ≤ 100 KB (companion tables are ~74 KB; allow margin). +- **Target:** ≥20% wall-time reduction on `time ./unittest` at `-O3` after commit #8. +- **Stretch:** ≥30% wall-time reduction. +- **Stop condition:** if Approach A alone delivers ≥30%, B is optional. If A+B underdeliver vs. 20%, re-evaluate before any further restructuring. From 9813f7183656c1a124c3c05b506e29cc538ecd3e Mon Sep 17 00:00:00 2001 From: Rijn Buve Date: Thu, 28 May 2026 09:16:50 +0200 Subject: [PATCH 19/31] docs: implementation plan for mapcodelib speed optimization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Step-by-step plan for Tasks 1-10 (baseline → A1-A5 → B3-B5 → results), each with explicit file/line targets, code blocks, expected outputs, and per-step commits. Also fixes a small inaccuracy in the spec: RECORD_CODEX stores the computed coDex value (10*(c/5)+(c%5+1)), not the raw flags & 31. Co-Authored-By: Claude Sonnet 4.6 --- .../plans/2026-05-28-mapcodelib-optimize.md | 1366 +++++++++++++++++ .../2026-05-28-mapcodelib-optimize-design.md | 2 +- 2 files changed, 1367 insertions(+), 1 deletion(-) create mode 100644 docs/superpowers/plans/2026-05-28-mapcodelib-optimize.md diff --git a/docs/superpowers/plans/2026-05-28-mapcodelib-optimize.md b/docs/superpowers/plans/2026-05-28-mapcodelib-optimize.md new file mode 100644 index 0000000..78c739e --- /dev/null +++ b/docs/superpowers/plans/2026-05-28-mapcodelib-optimize.md @@ -0,0 +1,1366 @@ +# mapcodelib speed optimization implementation plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Reduce `time ./unittest` wall time by 20–50% on `-O3` builds while preserving bit-exact output, strict portable C99/C11, and per-call memory footprint. + +**Architecture:** +- **Approach A** (Tasks 2–6): Local hot-path edits in `mapcoder.c` — flag caching, branch reordering, length-tracked string assembly, tighter base-31 encode, and early exits in repack/unpack. +- **Approach B** (Tasks 7–9): Precomputed static companion tables derived once from `TERRITORY_BOUNDARIES`; hot loops in `encoderEngine`/`decoderEngine` read from them instead of re-deriving from `flags` per iteration. + +**Tech Stack:** C99, GCC `-O3`, pthread (test-only), `time` (wall-clock measurement). + +**Spec:** `docs/superpowers/specs/2026-05-28-mapcodelib-optimize-design.md` + +**Branch:** `feat/optimize` (already created, currently at design-spec commit `740bfec`). + +--- + +## Conventions used across all tasks + +### Build command +```bash +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp/mapcodelib && gcc -O3 -c mapcoder.c +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp/test && gcc -O3 unittest.c -lm -lpthread -o unittest ../mapcodelib/mapcoder.o +``` + +### Test command (correctness) +```bash +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp/test && ./unittest +``` +Expected: final line `Unit tests passed`, exit code 0. + +### Timing command (performance) +```bash +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp/test +( time ./unittest ) 2>&1 | tail -3 # repeat 3 times, take best user time +``` +Each commit body records: best `user` time, and the percent-delta vs. Task 1 baseline. + +### Commit footer +Every commit includes: +``` +Co-Authored-By: Claude Sonnet 4.6 +``` + +### Test integrity +Any commit that fails `./unittest` (non-zero exit, non-zero error count, or missing "Unit tests passed" line) MUST be reverted before continuing. Do not push fixes on top — revert and reattempt. + +--- + +## File structure + +| File | Status | Responsibility | +|------|--------|----------------| +| `mapcodelib/mapcoder.c` | Modify | All optimization edits land here. Hot paths (`encoderEngine`, `decoderEngine`, `fitsInsideBoundaries`, `encodeBase31`, etc.) and new static-scope companion tables + their initializer. | +| `mapcodelib/internal_data.h` | Read-only | Source of truth for `TERRITORY_BOUNDARIES`, `MAPCODE_BOUNDARY_MAX`, `DATA_START`. Do NOT modify. | +| `mapcodelib/mapcoder.h` | Read-only | Public ABI. Do NOT modify. | +| `test/unittest.c` | Read-only | Correctness + timing harness. Do NOT modify. | + +All other files (CMake, scripts, docs) are untouched by this plan. + +--- + +## Task 1: Baseline measurement + +**Files:** +- No source changes. Single commit pins the baseline timing. + +- [ ] **Step 1: Build with -O3** + +```bash +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp/mapcodelib && gcc -O3 -c mapcoder.c +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp/test && gcc -O3 unittest.c -lm -lpthread -o unittest ../mapcodelib/mapcoder.o +``` + +Expected: clean compile, no warnings/errors. Both `.o` and `unittest` binary produced. + +- [ ] **Step 2: Verify tests pass** + +```bash +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp/test && ./unittest 2>&1 | tail -5 +``` + +Expected: last lines include `Unit tests passed`. Exit code 0. + +- [ ] **Step 3: Run three timed iterations, capture best `user` time** + +```bash +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp/test +for i in 1 2 3; do echo "=== run $i ==="; ( time ./unittest >/dev/null ) 2>&1 | grep -E '^(real|user|sys)'; done +``` + +Record the **best of three** `user` times — that is the baseline number for all subsequent comparisons. Note it down (call it `T0`). + +- [ ] **Step 4: Commit baseline marker** + +Create an empty commit so the baseline timing lives in git history: + +```bash +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp +git commit --allow-empty -m "$(cat <<'EOF' +chore: pin perf baseline for feat/optimize + +Measured `time ./unittest` on -O3 build (best of 3 runs): + user time = T0 = s + +All subsequent perf commits on this branch quote their best user time +and the cumulative delta vs. this baseline. + +Co-Authored-By: Claude Sonnet 4.6 +EOF +)" +``` + +Replace `` with the actual best-of-three user time from Step 3. + +--- + +## Task 2: A1 — Cache `flags` per iteration in hot loops + +**Goal:** Read `TERRITORY_BOUNDARIES[i].flags` once per loop iteration into a local; rewrite macro call sites to use the local. Six call sites in three functions. + +**Files:** +- Modify: `mapcodelib/mapcoder.c` — `encoderEngine` (around line 1487), `decoderEngine` (around line 2710), `firstNamelessRecord` (line 822), `countNamelessRecords` (line 835). + +- [ ] **Step 1: Edit `encoderEngine` inner loop body to cache flags** + +Open `mapcodelib/mapcoder.c`. Locate the loop at line 1487: + +```c +for (i = from; i <= upto; i++) { + if (fitsInsideBoundaries(&enc->coord32, TERRITORY_BOUNDARY(i))) { + if (IS_NAMELESS(i)) { +``` + +Replace this loop body (lines 1487–1542 inclusive) with a version that introduces a local `flags` cache. The transformation is mechanical: every `IS_NAMELESS(i)`, `IS_RESTRICTED(i)`, `IS_SPECIAL_SHAPE(i)`, `REC_TYPE(i)`, `coDex(i)`, `HEADER_LETTER(i)` inside the loop body becomes a local-variable read. Keep behavior identical: + +```c +for (i = from; i <= upto; i++) { + const int flags = TERRITORY_BOUNDARIES[i].flags; + if (fitsInsideBoundaries(&enc->coord32, TERRITORY_BOUNDARY(i))) { + const int isNameless = (flags & 64); + const int recType = (flags >> 7) & 3; + const int isRestricted = (flags & 512); + if (isNameless) { + encodeNameless(result, enc, ccode, extraDigits, i); + } + else if (recType > 1) { + encodeAutoHeader(result, enc, i, extraDigits); + } + else if ((i == upto) && isSubdivision(ccode)) { + // *** do a recursive call for the parent *** + encoderEngine(parentTerritoryOf(ccode), enc, stop_with_one_result, extraDigits, requiredEncoder, + ccode); + return; + } + else // must be grid + { + // skip IS_RESTRICTED records unless there already is a result + if (result_counter || !isRestricted) { + const int c = flags & 31; + const int codexLocal = 10 * (c / 5) + ((c % 5) + 1); + if (codexLocal < 54) { + const char headerletter = (char)((recType == 1) ? ENCODE_CHARS[(flags >> 11) & 31] : 0); + encodeGrid(result, enc, i, extraDigits, headerletter); + } + } + } + + // =========== handle result (if any) + if (*result) { + result_counter++; + + repackIfAllDigits(result, 0); + + if ((requiredEncoder < 0) || (requiredEncoder == i)) { + const enum Territory ccodeFinal = (ccode_override != TERRITORY_NONE ? ccode_override : ccode); + if (*result && enc->mapcodes && (enc->mapcodes->count < MAX_NR_OF_MAPCODE_RESULTS)) { + char* s = enc->mapcodes->mapcode[enc->mapcodes->count++]; + if (ccodeFinal == TERRITORY_AAA) { + // AAA is never shown with territory + strcpy(s, result); + } + else { + getTerritoryIsoName(s, ccodeFinal, 0); + strcat(s, " "); + strcat(s, result); + } + } + if (requiredEncoder == i) { + return; + } + } + if (stop_with_one_result) { + return; + } + *result = 0; // clear for next iteration + } + } +} // for i +``` + +Reasoning: identical semantics, but the compiler now has a guaranteed single load for `flags` regardless of how `IS_NAMELESS` etc. are written elsewhere, and the locally extracted bit values are SSA-friendly. + +- [ ] **Step 2: Edit `decoderEngine` inner loop body** + +Locate the loop at `mapcoder.c:2710`: + +```c +for (i = from; i <= upto; i++) { + const int codexi = coDex(i); + const int r = REC_TYPE(i); + if (r == 0) { + if (IS_NAMELESS(i)) { +``` + +Replace lines 2710–2800 with a version that caches `flags` once: + +```c +for (i = from; i <= upto; i++) { + const int flags = TERRITORY_BOUNDARIES[i].flags; + const int c = flags & 31; + const int codexi = 10 * (c / 5) + ((c % 5) + 1); + const int r = (flags >> 7) & 3; + if (r == 0) { + if (flags & 64) { // IS_NAMELESS + if (((codexi == 21) && (codex == 22)) || + ((codexi == 22) && (codex == 32)) || + ((codexi == 13) && (codex == 23))) { + err = decodeNameless(dec, i); + break; + } + } + else { + if ((codexi == codex) || ((codex == 22) && (codexi == 21))) { + err = decodeGrid(dec, i, 0); + + // first of all, make sure the zone fits the country + restrictZoneTo(&dec->zone, &dec->zone, TERRITORY_BOUNDARY(upto)); + + if ((err == ERR_OK) && (flags & 512)) { // IS_RESTRICTED + int nrZoneOverlaps = 0; + int j; + + // *** make sure decode fits somewhere *** + dec->result = getMidPointFractions(&dec->zone); + dec->coord32 = convertFractionsToCoord32(&dec->result); + for (j = i - 1; j >= from; j--) { + // look in previous rects + if (!IS_RESTRICTED(j)) { + if (fitsInsideBoundaries(&dec->coord32, TERRITORY_BOUNDARY(j))) { + nrZoneOverlaps = 1; + break; + } + } + } + + if (!nrZoneOverlaps) { + MapcodeZone zfound; + TerritoryBoundary prevu; + for (j = from; j < i; j++) { + // try all smaller rectangles j + if (!IS_RESTRICTED(j)) { + MapcodeZone z; + if (restrictZoneTo(&z, &dec->zone, TERRITORY_BOUNDARY(j))) { + nrZoneOverlaps++; + if (nrZoneOverlaps == 1) { + // first fit! remember... + zoneCopyFrom(&zfound, &z); + ASSERT(j <= MAPCODE_BOUNDARY_MAX); + memcpy(&prevu, TERRITORY_BOUNDARY(j), sizeof(TerritoryBoundary)); + } + else { + // nrZoneOverlaps >= 2 + // more than one hit + break; // give up + } + } + } // IS_RESTRICTED + } // for j + + // if several sub-areas intersect, just return the whole zone + // (the center of which may NOT re-encode to the same mapcode!) + if (nrZoneOverlaps == 1) { + // found exactly ONE intersection? + zoneCopyFrom(&dec->zone, &zfound); + } + } + + if (!nrZoneOverlaps) { + err = ERR_MAPCODE_UNDECODABLE; // type 3 "NLD L222.222" + } + } // *** make sure decode fits somewhere *** + break; + } + } + } + else if (r == 1) { + if (codex == codexi + 10 && ENCODE_CHARS[(flags >> 11) & 31] == *s) { + err = decodeGrid(dec, i, 1); + break; + } + } + else { + //r>1 + if (((codex == 23) && (codexi == 22)) || + ((codex == 33) && (codexi == 23))) { + err = decodeAutoHeader(dec, i); + break; + } + } +} // for +``` + +Note: the inner `j` loops continue to use the `IS_RESTRICTED(j)` macro (not the cached `flags`) because they index a different record `j`. Leaving them as-is is correct. + +- [ ] **Step 3: Edit `firstNamelessRecord`** + +Locate `mapcoder.c:822`: + +```c +static int firstNamelessRecord(const int m, const int firstcode) { + int i = m; + const int codexm = coDex(m); + ASSERT((0 <= m) && (m <= MAPCODE_BOUNDARY_MAX)); + ASSERT((0 <= firstcode) && (firstcode <= MAPCODE_BOUNDARY_MAX)); + while (i >= firstcode && coDex(i) == codexm && IS_NAMELESS(i)) { + i--; + } + return (i + 1); +} +``` + +Replace with: + +```c +static int firstNamelessRecord(const int m, const int firstcode) { + int i = m; + const int codexm = coDex(m); + ASSERT((0 <= m) && (m <= MAPCODE_BOUNDARY_MAX)); + ASSERT((0 <= firstcode) && (firstcode <= MAPCODE_BOUNDARY_MAX)); + while (i >= firstcode) { + const int flags = TERRITORY_BOUNDARIES[i].flags; + const int c = flags & 31; + const int codexi = 10 * (c / 5) + ((c % 5) + 1); + if (codexi != codexm || !(flags & 64)) { + break; + } + i--; + } + return (i + 1); +} +``` + +- [ ] **Step 4: Edit `countNamelessRecords`** + +Locate `mapcoder.c:835`: + +```c +static int countNamelessRecords(const int m, const int firstcode) { + const int first = firstNamelessRecord(m, firstcode); + const int codexm = coDex(m); + int last = m; + ASSERT((0 <= m) && (m <= MAPCODE_BOUNDARY_MAX)); + ASSERT((0 <= firstcode) && (firstcode <= MAPCODE_BOUNDARY_MAX)); + while (coDex(last) == codexm) { + last++; + } + ... +``` + +Replace the inner `while` loop with: + +```c +static int countNamelessRecords(const int m, const int firstcode) { + const int first = firstNamelessRecord(m, firstcode); + const int codexm = coDex(m); + int last = m; + ASSERT((0 <= m) && (m <= MAPCODE_BOUNDARY_MAX)); + ASSERT((0 <= firstcode) && (firstcode <= MAPCODE_BOUNDARY_MAX)); + while (1) { + const int c = TERRITORY_BOUNDARIES[last].flags & 31; + const int codexLast = 10 * (c / 5) + ((c % 5) + 1); + if (codexLast != codexm) { + break; + } + last++; + } + ASSERT((0 <= last) && (last <= MAPCODE_BOUNDARY_MAX)); + ASSERT(last >= first); + return (last - first); +} +``` + +- [ ] **Step 5: Build and run tests** + +```bash +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp/mapcodelib && gcc -O3 -c mapcoder.c +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp/test && gcc -O3 unittest.c -lm -lpthread -o unittest ../mapcodelib/mapcoder.o && ./unittest 2>&1 | tail -5 +``` + +Expected: clean compile, no warnings/errors. `./unittest` exits 0 with `Unit tests passed` in the output. + +If anything fails: revert the changes in `mapcodelib/mapcoder.c` and stop. Do not proceed to Step 6. + +- [ ] **Step 6: Measure timing (3× best)** + +```bash +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp/test +for i in 1 2 3; do echo "=== run $i ==="; ( time ./unittest >/dev/null ) 2>&1 | grep -E '^(real|user|sys)'; done +``` + +Record the best `user` time (call it `T2`). Compute `delta_pct = (T0 - T2) / T0 * 100`. + +- [ ] **Step 7: Commit** + +```bash +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp +git add mapcodelib/mapcoder.c +git commit -m "$(cat <<'EOF' +perf: A1 — cache flags per loop iteration in hot paths + +In encoderEngine and decoderEngine inner loops, read +TERRITORY_BOUNDARIES[i].flags once per iteration into a const local and +extract bit fields from it, instead of using flag-extraction macros that +each re-dereference the same memory. Same change in firstNamelessRecord +and countNamelessRecords. + +Bit-exact: macros stay defined and used in cold paths; only the inner +loop body call sites were rewritten to local reads. + + time ./unittest (best of 3, user): + baseline (T0) = s + after A1 (T2) = s + delta = % + +Co-Authored-By: Claude Sonnet 4.6 +EOF +)" +``` + +Replace `` with the recorded numbers from Step 6 and Task 1. + +--- + +## Task 3: A2 — Reorder checks in `fitsInsideBoundaries` + +**Goal:** Test longitude band first to short-circuit faster on the typical case (most territory rectangles are narrow in longitude relative to the planet). + +**Files:** +- Modify: `mapcodelib/mapcoder.c:602` + +- [ ] **Step 1: Edit `fitsInsideBoundaries`** + +Locate `mapcoder.c:602`: + +```c +static int fitsInsideBoundaries(const Point32* coord32, const TerritoryBoundary* b) { + ASSERT(coord32); + ASSERT(b); + return (b->miny <= coord32->latMicroDeg && + coord32->latMicroDeg < b->maxy && + isInRange(coord32->lonMicroDeg, b->minx, b->maxx)); +} +``` + +Replace with longitude-first ordering: + +```c +static int fitsInsideBoundaries(const Point32* coord32, const TerritoryBoundary* b) { + ASSERT(coord32); + ASSERT(b); + return (isInRange(coord32->lonMicroDeg, b->minx, b->maxx) && + b->miny <= coord32->latMicroDeg && + coord32->latMicroDeg < b->maxy); +} +``` + +Reasoning: `&&` short-circuits left to right. Longitude is the more selective dimension for typical territory rectangles, so the cheap `isInRange` call rejects most non-matches first. + +- [ ] **Step 2: Build and run tests** + +Same commands as Task 2 Step 5. Expected: clean compile, `Unit tests passed`. + +- [ ] **Step 3: Measure timing (3× best)** + +Same as Task 2 Step 6. Record best `user` time (`T3`). Compute cumulative delta vs. `T0`. + +- [ ] **Step 4: Commit** + +```bash +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp +git add mapcodelib/mapcoder.c +git commit -m "$(cat <<'EOF' +perf: A2 — reorder fitsInsideBoundaries to test longitude first + +Most territory rectangles are narrower in longitude than in latitude +relative to their bounding ranges, so testing longitude first short- +circuits faster on the typical reject case. + + time ./unittest (best of 3, user): + baseline = s + after A2 = s + delta = % cumulative + +Co-Authored-By: Claude Sonnet 4.6 +EOF +)" +``` + +--- + +## Task 4: A3 — Length-tracked result assembly in `encoderEngine` + +**Goal:** Replace `strcpy`+`strcat`+`strcat` pattern (which rescans the destination on each `strcat`) with length-tracked `memcpy` calls. + +**Files:** +- Modify: `mapcodelib/mapcoder.c` — block around lines 1521–1530 (post-A1 line numbers shift slightly; locate by content). + +- [ ] **Step 1: Locate the relevant block** + +Within `encoderEngine`, find the section starting with: + +```c +char* s = enc->mapcodes->mapcode[enc->mapcodes->count++]; +if (ccodeFinal == TERRITORY_AAA) { + // AAA is never shown with territory + strcpy(s, result); +} +else { + getTerritoryIsoName(s, ccodeFinal, 0); + strcat(s, " "); + strcat(s, result); +} +``` + +- [ ] **Step 2: Replace with length-tracked memcpy** + +Replace the entire `if (ccodeFinal == TERRITORY_AAA) { ... } else { ... }` block with: + +```c +char* s = enc->mapcodes->mapcode[enc->mapcodes->count++]; +if (ccodeFinal == TERRITORY_AAA) { + // AAA is never shown with territory + strcpy(s, result); +} +else { + getTerritoryIsoName(s, ccodeFinal, 0); + size_t isoLen = strlen(s); + size_t resultLen = strlen(result); + s[isoLen] = ' '; + memcpy(s + isoLen + 1, result, resultLen + 1); // +1 to include NUL +} +``` + +Reasoning: `strcat(s, " ")` does `strlen(s)` then writes 2 bytes; `strcat(s, result)` does another `strlen(s)` (now longer) then writes `strlen(result)+1` bytes. The replacement does one `strlen` per source string and a single byte write + memcpy. Output is byte-identical. + +Note: `` is already included; `size_t` is fine in C99 source. + +- [ ] **Step 3: Build and run tests** + +Same as Task 2 Step 5. + +- [ ] **Step 4: Measure timing (3× best)** + +Same as Task 2 Step 6. + +- [ ] **Step 5: Commit** + +```bash +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp +git add mapcodelib/mapcoder.c +git commit -m "$(cat <<'EOF' +perf: A3 — length-tracked result assembly in encoderEngine + +strcpy + strcat + strcat each re-scans the destination from the start +to find the null terminator. Replace with explicit strlen on each +source plus a single memcpy. + +Output bytes are unchanged. + + time ./unittest (best of 3, user): + baseline = s + after A3 = s + delta = % cumulative + +Co-Authored-By: Claude Sonnet 4.6 +EOF +)" +``` + +--- + +## Task 5: A4 — Tighten `encodeBase31` + +**Goal:** Eliminate the per-iteration `value % 31` + `value / 31` by combining via `__builtin_*`-free C: compute quotient once, derive remainder. This is one machine division per iteration instead of two (the compiler may already do this, but it's not guaranteed without `-fno-trapping-math` etc.). + +**Files:** +- Modify: `mapcodelib/mapcoder.c:1083` + +- [ ] **Step 1: Edit `encodeBase31`** + +Locate `mapcoder.c:1083`: + +```c +static void encodeBase31(char* result, int value, int nrchars) { + ASSERT(result); + ASSERT(nrchars >= 0); + result[nrchars] = 0; // zero-terminate! + while (nrchars > 0) { + nrchars--; + result[nrchars] = ENCODE_CHARS[value % 31]; + value /= 31; + } +} +``` + +Replace with: + +```c +static void encodeBase31(char* result, int value, int nrchars) { + ASSERT(result); + ASSERT(nrchars >= 0); + result[nrchars] = 0; // zero-terminate! + while (nrchars > 0) { + const int q = value / 31; + const int r = value - q * 31; + nrchars--; + result[nrchars] = ENCODE_CHARS[r]; + value = q; + } +} +``` + +Reasoning: For `int` with `value >= 0` (precondition by inspection — callers always pass non-negative computed offsets), `r = value - q*31` is equivalent to `value % 31` but uses one IDIV/MUL+SUB pair rather than potentially two division-class operations. Modern compilers often emit `imul` magic constants for division by 31 and do this optimization themselves; we make it explicit so it survives older toolchains and `-O3` -> `-O2` regressions. + +- [ ] **Step 2: Build and run tests** + +Same as Task 2 Step 5. + +- [ ] **Step 3: Measure timing (3× best)** + +Same as Task 2 Step 6. + +- [ ] **Step 4: Commit** + +```bash +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp +git add mapcodelib/mapcoder.c +git commit -m "$(cat <<'EOF' +perf: A4 — single division per iteration in encodeBase31 + +Compute quotient once, derive remainder via subtraction so the loop +has one division-class op per character rather than two. + + time ./unittest (best of 3, user): + baseline = s + after A4 = s + delta = % cumulative + +Co-Authored-By: Claude Sonnet 4.6 +EOF +)" +``` + +--- + +## Task 6: A5 — Tighten `repackIfAllDigits` early-exit + +**Goal:** The current implementation walks the string twice in many cases. We add a tightened first-pass that bails sooner. + +**Files:** +- Modify: `mapcodelib/mapcoder.c:892` + +- [ ] **Step 1: Read the current implementation in full** + +The function spans `mapcoder.c:892–930` (approx). Read it carefully — it already has an `alldigits` flag flipped to 0 on first non-digit, but it still continues the scan looking for `'.'`. Let's tighten it. + +Look at the current code: + +```c +static void repackIfAllDigits(char* input, const int aonly) { + char* s = input; + int alldigits = 1; // assume all digits + char* e; + char* dotpos = NULL; + ASSERT(input); + for (e = s; *e != 0 && *e != '-'; e++) { + if (*e < '0' || *e > '9') { + ... +``` + +- [ ] **Step 2: Read the rest of the function first** + +```bash +sed -n '892,935p' /Users/rijn/source/mapcode-foundation/mapcode-cpp/mapcodelib/mapcoder.c +``` + +The implementing engineer MUST read the full function before editing it. The optimization opportunity is to short-circuit when `alldigits` becomes 0 AND `dotpos` is already set — at that point we know the full structure and don't need to scan further within this loop. + +- [ ] **Step 3: Apply minimal early-exit** + +In the loop body, after `alldigits = 0;` is set inside the `if (*e < '0' || *e > '9')` branch, the function already has a `break` for the case `*e != '.'`. If `*e == '.'`, the loop continues, but once `alldigits` is 0 there's no more work the loop accomplishes other than setting `dotpos`. + +The cleanest tightening: after the existing `if (*e == '.')` block that assigns `dotpos`, if `alldigits` is already 0 and `dotpos` is non-null, `break`. + +Locate the existing `else if (*e == '.')` clause (it should look like): + +```c +else if (*e == '.') { + dotpos = e; +} +``` + +Modify the surrounding `for` loop body so that after `dotpos = e;` we check both conditions and break when no useful work remains. Replace the entire `for` loop with: + +```c +for (e = s; *e != 0 && *e != '-'; e++) { + if (*e < '0' || *e > '9') { + if (*e == '.') { + if (dotpos) { + // Two dots: malformed for repack purposes, bail. + return; + } + dotpos = e; + if (!alldigits) { + break; // structure fully known; nothing else to learn here + } + } else { + alldigits = 0; + if (dotpos) { + break; // structure fully known + } + } + } +} +``` + +Be very careful: the original code may handle the "two dots" case differently. If the actual code differs from what is described here, ABORT this task and consult the maintainer. Do NOT change the semantics for any input. + +- [ ] **Step 4: Read the function again to confirm semantics** + +Before committing, the implementer MUST diff the file against `git HEAD` and ensure the only changes are: +1. Optional `break`s added in well-understood control-flow locations. +2. No other behavioral change. + +If the diff shows anything else changed (variable names, condition rewrites, etc.), revert. + +- [ ] **Step 5: Build and run tests** + +Same as Task 2 Step 5. Bit-exactness is essential here — if any single test fails, revert immediately. + +- [ ] **Step 6: Measure timing (3× best)** + +Same as Task 2 Step 6. + +- [ ] **Step 7: Commit (or skip if no measurable gain)** + +If the timing delta vs. previous commit is below noise threshold (< 0.5%) and the diff is non-trivial, prefer NOT to commit (skipping A5 is acceptable per the spec). Otherwise: + +```bash +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp +git add mapcodelib/mapcoder.c +git commit -m "$(cat <<'EOF' +perf: A5 — early-exit when structure is fully known in repackIfAllDigits + +Break out of the scan loop once both the digits-only status and the +dot position are determined. Same input/output mapping; the loop just +stops earlier on common inputs. + + time ./unittest (best of 3, user): + baseline = s + after A5 = s + delta = % cumulative + +Co-Authored-By: Claude Sonnet 4.6 +EOF +)" +``` + +If skipped, document the skip in the next commit's body ("A5 skipped: gain below noise"). + +--- + +## Task 7: B1+B2+B3 — Define and initialize companion tables (not yet used) + +**Goal:** Add companion static tables, a one-time initializer, and call sites that invoke the initializer at every public entry point. The hot loops still use macros at this point — this task is verified by tests-still-pass + the initializer running without crashing. + +**Files:** +- Modify: `mapcodelib/mapcoder.c` — add static-scope tables and `initCompanionTables()` near the top of the file (after macros, before any hot function). + +- [ ] **Step 1: Add static companion tables and initializer** + +Open `mapcodelib/mapcoder.c`. Find the line right after the flag-extraction macro definitions (after `mapcoder.c:154` `#define HEADER_LETTER(m) ...`). Add the following block: + +```c +/////////////////////////////////////////////////////////////////////////////////////////////// +// +// PRECOMPUTED COMPANION TABLES (perf: feat/optimize, see docs/superpowers/specs/2026-05-28-mapcodelib-optimize-design.md) +// +// These tables are derived once from TERRITORY_BOUNDARIES.flags and queried +// in hot encode/decode loops to avoid repeated bit-masking on the same record. +// +/////////////////////////////////////////////////////////////////////////////////////////////// + +#define KIND_BIT_NAMELESS 0x01 +#define KIND_BIT_RESTRICTED 0x02 +#define KIND_BIT_SPECIAL_SHAPE 0x04 + +static unsigned char RECORD_CODEX[MAPCODE_BOUNDARY_MAX + 1]; // = coDex(m) +static unsigned char RECORD_REC_TYPE[MAPCODE_BOUNDARY_MAX + 1]; // (flags >> 7) & 3 +static unsigned char RECORD_KIND[MAPCODE_BOUNDARY_MAX + 1]; // KIND_BIT_* bitwise OR +static unsigned char RECORD_HEADER_LETTER[MAPCODE_BOUNDARY_MAX + 1]; // ENCODE_CHARS[(flags >> 11) & 31] +static unsigned short RECORD_SMART_DIV[MAPCODE_BOUNDARY_MAX + 1]; // flags >> 16 + +// Per-territory: index of the first nameless record (or -1), and how many. +// These tables are sized to the full territory enum range. +#define TERRITORY_TABLE_SIZE (_TERRITORY_MAX - _TERRITORY_MIN) +static int TERRITORY_FIRST_NAMELESS[TERRITORY_TABLE_SIZE]; +static int TERRITORY_NAMELESS_COUNT[TERRITORY_TABLE_SIZE]; + +// Init flag. Write-once after first call. Idempotent: deterministic values mean +// a race on first call only causes duplicate work, never wrong results. +// The library does not promise thread-safety on first call (see header docs). +static int companion_initialized = 0; + +static void initCompanionTables(void) { + int m; + int t; + if (companion_initialized) { + return; + } + for (m = 0; m <= MAPCODE_BOUNDARY_MAX; m++) { + const int flags = TERRITORY_BOUNDARIES[m].flags; + const int c = flags & 31; + const int codex_val = 10 * (c / 5) + ((c % 5) + 1); + const int rec_type = (flags >> 7) & 3; + unsigned char kind = 0; + if (flags & 64) kind |= KIND_BIT_NAMELESS; + if (flags & 512) kind |= KIND_BIT_RESTRICTED; + if (flags & 1024) kind |= KIND_BIT_SPECIAL_SHAPE; + RECORD_CODEX[m] = (unsigned char) codex_val; + RECORD_REC_TYPE[m] = (unsigned char) rec_type; + RECORD_KIND[m] = kind; + RECORD_HEADER_LETTER[m] = (unsigned char) ENCODE_CHARS[(flags >> 11) & 31]; + RECORD_SMART_DIV[m] = (unsigned short) ((unsigned int) flags >> 16); + } + for (t = 0; t < TERRITORY_TABLE_SIZE; t++) { + TERRITORY_FIRST_NAMELESS[t] = -1; + TERRITORY_NAMELESS_COUNT[t] = 0; + } + for (t = 0; t < TERRITORY_TABLE_SIZE - 1; t++) { + const int from = DATA_START[t]; + const int upto_excl = DATA_START[t + 1]; + int i; + int first = -1; + int count = 0; + for (i = from; i < upto_excl; i++) { + if (RECORD_KIND[i] & KIND_BIT_NAMELESS) { + if (first < 0) { + first = i; + } + count++; + } + } + TERRITORY_FIRST_NAMELESS[t] = first; + TERRITORY_NAMELESS_COUNT[t] = count; + } + companion_initialized = 1; +} +``` + +Placement: insert this block immediately after the `HEADER_LETTER` macro (line 154) and BEFORE any function definitions. It depends only on `TERRITORY_BOUNDARIES` and `ENCODE_CHARS` (defined further down) — so be careful: `ENCODE_CHARS` is defined at `mapcoder.c:332`. The initializer references it. Since `initCompanionTables` is a function (not a static initializer), it's evaluated at call time, after `ENCODE_CHARS` is fully visible in the translation unit. C99 allows a function to reference any file-scope identifier as long as the identifier is declared before the function's first call. We must therefore either: + - Insert this block AFTER `ENCODE_CHARS` (i.e. after line 332). RECOMMENDED. + - Or insert a forward declaration of `ENCODE_CHARS` earlier in the file. + +Use the RECOMMENDED option: place the entire companion-tables block immediately AFTER the `ENCODE_CHARS` definition (around line 332+, before `decodeChar` at line 352). + +- [ ] **Step 2: Call `initCompanionTables()` from public entry points** + +The public encode/decode entry points are: +- `encodeLatLonToMapcodes_internal` (`mapcoder.c:1549`) +- `decoderEngine` (`mapcoder.c:2651`) + +At the very top of `encodeLatLonToMapcodes_internal` (after the `ASSERT(mapcodes);` etc., before `convertCoordsToMicrosAndFractions`), add: + +```c +initCompanionTables(); +``` + +At the very top of `decoderEngine` (after `ASSERT(dec);`, before `parseMapcodeString`), add: + +```c +initCompanionTables(); +``` + +Both calls are guarded by the cheap `if (companion_initialized) return;` so the call cost is one branch per call after the first. + +- [ ] **Step 3: Build and run tests** + +```bash +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp/mapcodelib && gcc -O3 -c mapcoder.c +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp/test && gcc -O3 unittest.c -lm -lpthread -o unittest ../mapcodelib/mapcoder.o && ./unittest 2>&1 | tail -5 +``` + +Expected: clean compile (no unused-variable warnings on the new tables — they ARE used by the initializer), `Unit tests passed`. If anything fails, revert. + +- [ ] **Step 4: Measure timing (3× best)** + +Same as Task 2 Step 6. The expected delta vs. previous commit is tiny (near zero or slightly negative due to init cost on first call). That's expected; B4 is where the payoff lands. + +- [ ] **Step 5: Commit** + +```bash +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp +git add mapcodelib/mapcoder.c +git commit -m "$(cat <<'EOF' +perf: B1+B2+B3 — add precomputed companion tables and one-time init + +Static tables RECORD_CODEX / RECORD_REC_TYPE / RECORD_KIND / +RECORD_HEADER_LETTER / RECORD_SMART_DIV are precomputed once from +TERRITORY_BOUNDARIES.flags. Per-territory TERRITORY_FIRST_NAMELESS / +TERRITORY_NAMELESS_COUNT replace linear scans. + +This commit only defines the tables and calls initCompanionTables at +the top of encodeLatLonToMapcodes_internal and decoderEngine. Hot loops +still use the existing macros; the switch happens in B4. + +Memory footprint: ~74 KB of additional static data. + + time ./unittest (best of 3, user): + baseline = s + after B3 = s + delta = % cumulative + +Co-Authored-By: Claude Sonnet 4.6 +EOF +)" +``` + +--- + +## Task 8: B4 — Hot loops read from companion tables + +**Goal:** The payoff commit. Switch the inner-loop reads in `encoderEngine`, `decoderEngine`, `firstNamelessRecord`, `countNamelessRecords` from the local `flags` cache (added in A1) to the precomputed companion tables. + +**Files:** +- Modify: `mapcodelib/mapcoder.c` — same loop bodies edited in Task 2 (A1), plus the two nameless helpers. + +- [ ] **Step 1: Replace `encoderEngine` loop body to use companion tables** + +Locate the loop edited in Task 2 (the `for (i = from; i <= upto; i++)` in `encoderEngine`). Replace the local `flags`-based extractions with direct table reads: + +```c +for (i = from; i <= upto; i++) { + const unsigned char kind = RECORD_KIND[i]; + const unsigned char recTypeI = RECORD_REC_TYPE[i]; + if (fitsInsideBoundaries(&enc->coord32, TERRITORY_BOUNDARY(i))) { + if (kind & KIND_BIT_NAMELESS) { + encodeNameless(result, enc, ccode, extraDigits, i); + } + else if (recTypeI > 1) { + encodeAutoHeader(result, enc, i, extraDigits); + } + else if ((i == upto) && isSubdivision(ccode)) { + encoderEngine(parentTerritoryOf(ccode), enc, stop_with_one_result, extraDigits, requiredEncoder, + ccode); + return; + } + else // must be grid + { + if (result_counter || !(kind & KIND_BIT_RESTRICTED)) { + if (RECORD_CODEX[i] < 54) { + const char headerletter = (char)((recTypeI == 1) ? RECORD_HEADER_LETTER[i] : 0); + encodeGrid(result, enc, i, extraDigits, headerletter); + } + } + } + + // =========== handle result (if any) + if (*result) { + result_counter++; + + repackIfAllDigits(result, 0); + + if ((requiredEncoder < 0) || (requiredEncoder == i)) { + const enum Territory ccodeFinal = (ccode_override != TERRITORY_NONE ? ccode_override : ccode); + if (*result && enc->mapcodes && (enc->mapcodes->count < MAX_NR_OF_MAPCODE_RESULTS)) { + char* s = enc->mapcodes->mapcode[enc->mapcodes->count++]; + if (ccodeFinal == TERRITORY_AAA) { + strcpy(s, result); + } + else { + getTerritoryIsoName(s, ccodeFinal, 0); + size_t isoLen = strlen(s); + size_t resultLen = strlen(result); + s[isoLen] = ' '; + memcpy(s + isoLen + 1, result, resultLen + 1); + } + } + if (requiredEncoder == i) { + return; + } + } + if (stop_with_one_result) { + return; + } + *result = 0; + } + } +} // for i +``` + +The local `const int flags = ...` line introduced in A1 is removed (no longer used inside this loop). + +- [ ] **Step 2: Replace `decoderEngine` loop body to use companion tables** + +Locate the corresponding loop in `decoderEngine`. Replace with: + +```c +for (i = from; i <= upto; i++) { + const unsigned char kind = RECORD_KIND[i]; + const int codexi = RECORD_CODEX[i]; + const int r = RECORD_REC_TYPE[i]; + if (r == 0) { + if (kind & KIND_BIT_NAMELESS) { + if (((codexi == 21) && (codex == 22)) || + ((codexi == 22) && (codex == 32)) || + ((codexi == 13) && (codex == 23))) { + err = decodeNameless(dec, i); + break; + } + } + else { + if ((codexi == codex) || ((codex == 22) && (codexi == 21))) { + err = decodeGrid(dec, i, 0); + + // first of all, make sure the zone fits the country + restrictZoneTo(&dec->zone, &dec->zone, TERRITORY_BOUNDARY(upto)); + + if ((err == ERR_OK) && (kind & KIND_BIT_RESTRICTED)) { + int nrZoneOverlaps = 0; + int j; + + dec->result = getMidPointFractions(&dec->zone); + dec->coord32 = convertFractionsToCoord32(&dec->result); + for (j = i - 1; j >= from; j--) { + if (!(RECORD_KIND[j] & KIND_BIT_RESTRICTED)) { + if (fitsInsideBoundaries(&dec->coord32, TERRITORY_BOUNDARY(j))) { + nrZoneOverlaps = 1; + break; + } + } + } + + if (!nrZoneOverlaps) { + MapcodeZone zfound; + TerritoryBoundary prevu; + for (j = from; j < i; j++) { + if (!(RECORD_KIND[j] & KIND_BIT_RESTRICTED)) { + MapcodeZone z; + if (restrictZoneTo(&z, &dec->zone, TERRITORY_BOUNDARY(j))) { + nrZoneOverlaps++; + if (nrZoneOverlaps == 1) { + zoneCopyFrom(&zfound, &z); + ASSERT(j <= MAPCODE_BOUNDARY_MAX); + memcpy(&prevu, TERRITORY_BOUNDARY(j), sizeof(TerritoryBoundary)); + } + else { + break; + } + } + } + } + + if (nrZoneOverlaps == 1) { + zoneCopyFrom(&dec->zone, &zfound); + } + } + + if (!nrZoneOverlaps) { + err = ERR_MAPCODE_UNDECODABLE; + } + } + break; + } + } + } + else if (r == 1) { + if (codex == codexi + 10 && RECORD_HEADER_LETTER[i] == (unsigned char) *s) { + err = decodeGrid(dec, i, 1); + break; + } + } + else { + if (((codex == 23) && (codexi == 22)) || + ((codex == 33) && (codexi == 23))) { + err = decodeAutoHeader(dec, i); + break; + } + } +} // for +``` + +The inner `j` loops now also use `RECORD_KIND[j] & KIND_BIT_RESTRICTED` (this is a small extra win since the same loop ran the `IS_RESTRICTED(j)` macro repeatedly). + +- [ ] **Step 3: Replace `firstNamelessRecord` and `countNamelessRecords` to use tables** + +For `firstNamelessRecord`: + +```c +static int firstNamelessRecord(const int m, const int firstcode) { + int i = m; + const int codexm = RECORD_CODEX[m]; + ASSERT((0 <= m) && (m <= MAPCODE_BOUNDARY_MAX)); + ASSERT((0 <= firstcode) && (firstcode <= MAPCODE_BOUNDARY_MAX)); + while (i >= firstcode && RECORD_CODEX[i] == codexm && (RECORD_KIND[i] & KIND_BIT_NAMELESS)) { + i--; + } + return (i + 1); +} +``` + +For `countNamelessRecords`: + +```c +static int countNamelessRecords(const int m, const int firstcode) { + const int first = firstNamelessRecord(m, firstcode); + const int codexm = RECORD_CODEX[m]; + int last = m; + ASSERT((0 <= m) && (m <= MAPCODE_BOUNDARY_MAX)); + ASSERT((0 <= firstcode) && (firstcode <= MAPCODE_BOUNDARY_MAX)); + while (RECORD_CODEX[last] == codexm) { + last++; + } + ASSERT((0 <= last) && (last <= MAPCODE_BOUNDARY_MAX)); + ASSERT(last >= first); + return (last - first); +} +``` + +- [ ] **Step 4: Build and run tests** + +```bash +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp/mapcodelib && gcc -O3 -c mapcoder.c +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp/test && gcc -O3 unittest.c -lm -lpthread -o unittest ../mapcodelib/mapcoder.o && ./unittest 2>&1 | tail -5 +``` + +Expected: `Unit tests passed`. Any failure → immediate revert. + +- [ ] **Step 5: Measure timing (3× best)** + +Same as Task 2 Step 6. This should show the largest single-commit speedup. + +- [ ] **Step 6: Commit** + +```bash +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp +git add mapcodelib/mapcoder.c +git commit -m "$(cat <<'EOF' +perf: B4 — hot loops read from precomputed companion tables + +Replace per-iteration mask/shift extractions in encoderEngine, +decoderEngine, firstNamelessRecord, countNamelessRecords with direct +reads from RECORD_KIND / RECORD_CODEX / RECORD_REC_TYPE / +RECORD_HEADER_LETTER. The companion tables are byte-sized so each +field is a single byte load with friendlier cache behavior than the +4-byte flags field. + +Values are derived from the same macros they replace (see B3 init); +output is bit-exact. + + time ./unittest (best of 3, user): + baseline = s + after B4 = s + delta = % cumulative + +Co-Authored-By: Claude Sonnet 4.6 +EOF +)" +``` + +--- + +## Task 9: B5 — Debug-build sanity check + +**Goal:** In `#ifdef DEBUG` builds only, have `initCompanionTables` assert that every precomputed value matches the macro-derived value. Free correctness guarantee during development. + +**Files:** +- Modify: `mapcodelib/mapcoder.c` — the `initCompanionTables` function. + +- [ ] **Step 1: Add the debug check at the end of `initCompanionTables`** + +Just before `companion_initialized = 1;` (the last line of `initCompanionTables`), add: + +```c +#ifdef DEBUG + for (m = 0; m <= MAPCODE_BOUNDARY_MAX; m++) { + const int flags = TERRITORY_BOUNDARIES[m].flags; + const int c = flags & 31; + const int codex_val = 10 * (c / 5) + ((c % 5) + 1); + ASSERT(RECORD_CODEX[m] == (unsigned char) codex_val); + ASSERT(RECORD_REC_TYPE[m] == (unsigned char) ((flags >> 7) & 3)); + ASSERT(((RECORD_KIND[m] & KIND_BIT_NAMELESS) != 0) == ((flags & 64) != 0)); + ASSERT(((RECORD_KIND[m] & KIND_BIT_RESTRICTED) != 0) == ((flags & 512) != 0)); + ASSERT(((RECORD_KIND[m] & KIND_BIT_SPECIAL_SHAPE) != 0) == ((flags & 1024) != 0)); + ASSERT(RECORD_HEADER_LETTER[m] == (unsigned char) ENCODE_CHARS[(flags >> 11) & 31]); + ASSERT(RECORD_SMART_DIV[m] == (unsigned short) ((unsigned int) flags >> 16)); + } +#endif +``` + +(`m` is already declared at the top of `initCompanionTables`, so no new declarations needed.) + +- [ ] **Step 2: Build and run tests in BOTH O3 and DEBUG modes** + +```bash +# -O3 release (no debug assertions) +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp/mapcodelib && gcc -O3 -c mapcoder.c +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp/test && gcc -O3 unittest.c -lm -lpthread -o unittest ../mapcodelib/mapcoder.o && ./unittest 2>&1 | tail -5 + +# DEBUG build (with assertions) +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp/mapcodelib && gcc -O0 -DDEBUG -c mapcoder.c +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp/test && gcc -O0 -DDEBUG unittest.c -lm -lpthread -o unittest ../mapcodelib/mapcoder.o && ./unittest 2>&1 | tail -5 +``` + +Both must report `Unit tests passed`. The DEBUG run additionally exercises the new `ASSERT` block in `initCompanionTables` — if any precomputed value diverges from the macro-derived one, the DEBUG build aborts (which is exactly what we want). + +- [ ] **Step 3: Re-build -O3 (so the binary in test/ is the release one for timing)** + +```bash +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp/mapcodelib && gcc -O3 -c mapcoder.c +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp/test && gcc -O3 unittest.c -lm -lpthread -o unittest ../mapcodelib/mapcoder.o +``` + +- [ ] **Step 4: Measure timing (3× best)** + +Same as Task 2 Step 6. Expected: delta from previous commit is near zero (the new code is `#ifdef DEBUG` gated and absent in -O3). + +- [ ] **Step 5: Commit** + +```bash +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp +git add mapcodelib/mapcoder.c +git commit -m "$(cat <<'EOF' +perf: B5 — DEBUG-build sanity check for companion tables + +Under -DDEBUG, initCompanionTables asserts that every precomputed value +matches the macro-derived one. Free correctness guard during development; +zero cost in release. + +Verified: both -O3 and -O0 -DDEBUG builds pass the unit suite. + + time ./unittest -O3 (best of 3, user): + baseline = s + after B5 = s + delta = % cumulative + +Co-Authored-By: Claude Sonnet 4.6 +EOF +)" +``` + +--- + +## Task 10: Final summary commit (mandatory) + +**Goal:** Single commit summarizing the full optimization arc: baseline, per-step deltas, and the cumulative win. Useful for review and for future archaeology. + +**Files:** +- Modify: `docs/superpowers/specs/2026-05-28-mapcodelib-optimize-design.md` — append a results section. + +- [ ] **Step 1: Append a results section to the spec** + +Open `docs/superpowers/specs/2026-05-28-mapcodelib-optimize-design.md` and append at the bottom: + +```markdown +## 8. Results + +Measurements taken on branch `feat/optimize`, `-O3` build, `time ./unittest` +(best `user` of 3 back-to-back runs). + +| Commit | Description | Best user time | Delta vs. baseline | +|--------|-------------|---------------|--------------------| +| Task 1 | baseline | s | 0.0% | +| Task 2 | A1 — cache flags per iteration | s | % | +| Task 3 | A2 — reorder fitsInsideBoundaries | s | % | +| Task 4 | A3 — length-tracked result assembly | s | % | +| Task 5 | A4 — tighten encodeBase31 | s | % | +| Task 6 | A5 — repack early-exit | s | % | +| Task 7 | B3 — companion tables init (no hot-path use yet) | s | % | +| Task 8 | B4 — hot loops use companion tables | s | % | +| Task 9 | B5 — DEBUG sanity check | s | % | + +**Final cumulative speedup:** % + +**Success criteria check:** +- [ ] All unit tests passing after every commit (verified) +- [ ] Binary size growth ≤ 100 KB (verified) +- [ ] ≥20% wall-time reduction (target — record whether achieved) +- [ ] ≥30% wall-time reduction (stretch — record whether achieved) +``` + +Fill in all `` values from the prior commit messages. + +- [ ] **Step 2: Verify binary size delta** + +```bash +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp +git stash || true +git checkout 740bfec -- mapcodelib/mapcoder.c +cd mapcodelib && gcc -O3 -c mapcoder.c && BASELINE_SIZE=$(stat -f%z mapcoder.o) +cd .. +git checkout HEAD -- mapcodelib/mapcoder.c +git stash pop 2>/dev/null || true +cd mapcodelib && gcc -O3 -c mapcoder.c && OPTIMIZED_SIZE=$(stat -f%z mapcoder.o) +echo "Baseline .o: $BASELINE_SIZE bytes" +echo "Optimized .o: $OPTIMIZED_SIZE bytes" +echo "Delta: $((OPTIMIZED_SIZE - BASELINE_SIZE)) bytes" +``` + +If delta > 100,000 bytes (100 KB) the optimization violated a hard constraint — investigate before committing. + +- [ ] **Step 3: Final test pass** + +```bash +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp/test && ./unittest 2>&1 | tail -5 +``` + +Expected: `Unit tests passed`. + +- [ ] **Step 4: Commit summary** + +```bash +cd /Users/rijn/source/mapcode-foundation/mapcode-cpp +git add docs/superpowers/specs/2026-05-28-mapcodelib-optimize-design.md +git commit -m "$(cat <<'EOF' +docs: record final perf results for feat/optimize branch + +Appends a results table to the design spec capturing per-commit timing +deltas and the cumulative speedup vs. the Task 1 baseline. + +Final speedup: % wall-time on `time ./unittest` at -O3. + +Co-Authored-By: Claude Sonnet 4.6 +EOF +)" +``` + +--- + +## Self-review + +Spec coverage check: +- §1 scope/goals → covered by all tasks; bit-exact guardrail enforced by Step "run tests" in every task. +- §2 measurement methodology → Task 1 establishes baseline; every subsequent task records timing. +- §3 Approach A (A1–A5) → Tasks 2–6. +- §4 Approach B (B1–B5) → Tasks 7–9. +- §4 "Interaction with A1" → Task 8 Step 1 explicitly removes the A1 local `flags` cache when switching to companion tables. +- §5 workflow → matches Tasks 1–9. +- §6 risks → mitigated by "test after every step + revert on failure" in each task; debug-build assertion in Task 9. +- §7 success criteria → Task 10 verifies binary size and produces the final results table. + +Placeholder scan: `` markers are intentional — they're per-run measurements the implementer fills in at commit time. No `TBD`/`TODO`/"implement later" anywhere. + +Type consistency: `unsigned char` used uniformly for KIND/CODEX/REC_TYPE/HEADER_LETTER; `unsigned short` for SMART_DIV; `int` for TERRITORY_FIRST_NAMELESS/COUNT (matches existing code conventions for record indices). Names `KIND_BIT_NAMELESS` / `KIND_BIT_RESTRICTED` / `KIND_BIT_SPECIAL_SHAPE` used consistently across Tasks 7 and 8. diff --git a/docs/superpowers/specs/2026-05-28-mapcodelib-optimize-design.md b/docs/superpowers/specs/2026-05-28-mapcodelib-optimize-design.md index f351078..7f24f2b 100644 --- a/docs/superpowers/specs/2026-05-28-mapcodelib-optimize-design.md +++ b/docs/superpowers/specs/2026-05-28-mapcodelib-optimize-design.md @@ -101,7 +101,7 @@ Add to `mapcoder.c` at file scope (not exported): ```c // One byte per record. Initialized once via initCompanionTables(). -static unsigned char RECORD_CODEX[MAPCODE_BOUNDARY_MAX + 1]; // flags & 31 +static unsigned char RECORD_CODEX[MAPCODE_BOUNDARY_MAX + 1]; // = coDex(m): 10*(c/5)+(c%5+1), c = flags & 31 static unsigned char RECORD_REC_TYPE[MAPCODE_BOUNDARY_MAX + 1]; // (flags >> 7) & 3 static unsigned char RECORD_KIND[MAPCODE_BOUNDARY_MAX + 1]; // bit0=nameless, bit1=restricted, // bit2=specialShape, bit3=hasHeaderLetter From f1cb7368ff606dee01598c8a94619a944f6d010b Mon Sep 17 00:00:00 2001 From: Rijn Buve Date: Thu, 28 May 2026 09:25:52 +0200 Subject: [PATCH 20/31] chore: pin perf baseline for feat/optimize Measured `time ./unittest` on -O3 build (best of 3 runs): user time = T0 = 114.13s All subsequent perf commits on this branch quote their best user time and the cumulative delta vs. this baseline. Co-Authored-By: Claude Sonnet 4.6 From 430bbba539af1b4961901c1712350b69c9b8144e Mon Sep 17 00:00:00 2001 From: Rijn Buve Date: Thu, 28 May 2026 09:32:49 +0200 Subject: [PATCH 21/31] =?UTF-8?q?perf:=20A1=20=E2=80=94=20cache=20flags=20?= =?UTF-8?q?per=20loop=20iteration=20in=20hot=20paths?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In encoderEngine and decoderEngine inner loops, read TERRITORY_BOUNDARIES[i].flags once per iteration into a const local and extract bit fields from it, instead of using flag-extraction macros that each re-dereference the same memory. Same change in firstNamelessRecord and countNamelessRecords. Bit-exact: macros stay defined and used in cold paths; only the inner loop body call sites were rewritten to local reads. time ./unittest (best of 3, user): baseline (T0) = 114.13s after A1 (T2) = 111.78s delta = 2.06% cumulative Co-Authored-By: Claude Sonnet 4.6 --- mapcodelib/mapcoder.c | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/mapcodelib/mapcoder.c b/mapcodelib/mapcoder.c index e7b0a4d..4ef59d8 100644 --- a/mapcodelib/mapcoder.c +++ b/mapcodelib/mapcoder.c @@ -824,7 +824,13 @@ static int firstNamelessRecord(const int m, const int firstcode) { const int codexm = coDex(m); ASSERT((0 <= m) && (m <= MAPCODE_BOUNDARY_MAX)); ASSERT((0 <= firstcode) && (firstcode <= MAPCODE_BOUNDARY_MAX)); - while (i >= firstcode && coDex(i) == codexm && IS_NAMELESS(i)) { + while (i >= firstcode) { + const int flags = TERRITORY_BOUNDARIES[i].flags; + const int c = flags & 31; + const int codexi = 10 * (c / 5) + ((c % 5) + 1); + if (codexi != codexm || !(flags & 64)) { + break; + } i--; } return (i + 1); @@ -838,7 +844,12 @@ static int countNamelessRecords(const int m, const int firstcode) { int last = m; ASSERT((0 <= m) && (m <= MAPCODE_BOUNDARY_MAX)); ASSERT((0 <= firstcode) && (firstcode <= MAPCODE_BOUNDARY_MAX)); - while (coDex(last) == codexm) { + while (1) { + const int c = TERRITORY_BOUNDARIES[last].flags & 31; + const int codexLast = 10 * (c / 5) + ((c % 5) + 1); + if (codexLast != codexm) { + break; + } last++; } ASSERT((0 <= last) && (last <= MAPCODE_BOUNDARY_MAX)); @@ -1486,10 +1497,14 @@ static void encoderEngine(const enum Territory ccode, const EncodeRec* enc, cons *result = 0; for (i = from; i <= upto; i++) { if (fitsInsideBoundaries(&enc->coord32, TERRITORY_BOUNDARY(i))) { - if (IS_NAMELESS(i)) { + const int flags = TERRITORY_BOUNDARIES[i].flags; + const int isNameless = (flags & 64); + const int recType = (flags >> 7) & 3; + const int isRestricted = (flags & 512); + if (isNameless) { encodeNameless(result, enc, ccode, extraDigits, i); } - else if (REC_TYPE(i) > 1) { + else if (recType > 1) { encodeAutoHeader(result, enc, i, extraDigits); } else if ((i == upto) && isSubdivision(ccode)) { @@ -1501,9 +1516,11 @@ static void encoderEngine(const enum Territory ccode, const EncodeRec* enc, cons else // must be grid { // skip IS_RESTRICTED records unless there already is a result - if (result_counter || !IS_RESTRICTED(i)) { - if (coDex(i) < 54) { - char headerletter = (char)((REC_TYPE(i) == 1) ? HEADER_LETTER(i) : 0); + if (result_counter || !isRestricted) { + const int c = flags & 31; + const int codexLocal = 10 * (c / 5) + ((c % 5) + 1); + if (codexLocal < 54) { + const char headerletter = (char)((recType == 1) ? ENCODE_CHARS[(flags >> 11) & 31] : 0); encodeGrid(result, enc, i, extraDigits, headerletter); } } @@ -2708,10 +2725,12 @@ static enum MapcodeError decoderEngine(DecodeRec* dec, int parseFlags) { // try all ccode rectangles to decode s (pointing to first character of proper mapcode), assume not decodable err = ERR_MAPCODE_UNDECODABLE; for (i = from; i <= upto; i++) { - const int codexi = coDex(i); - const int r = REC_TYPE(i); + const int flags = TERRITORY_BOUNDARIES[i].flags; + const int c = flags & 31; + const int codexi = 10 * (c / 5) + ((c % 5) + 1); + const int r = (flags >> 7) & 3; if (r == 0) { - if (IS_NAMELESS(i)) { + if (flags & 64) { // IS_NAMELESS if (((codexi == 21) && (codex == 22)) || ((codexi == 22) && (codex == 32)) || ((codexi == 13) && (codex == 23))) { @@ -2726,7 +2745,7 @@ static enum MapcodeError decoderEngine(DecodeRec* dec, int parseFlags) { // first of all, make sure the zone fits the country restrictZoneTo(&dec->zone, &dec->zone, TERRITORY_BOUNDARY(upto)); - if ((err == ERR_OK) && IS_RESTRICTED(i)) { + if ((err == ERR_OK) && (flags & 512)) { // IS_RESTRICTED int nrZoneOverlaps = 0; int j; @@ -2784,7 +2803,7 @@ static enum MapcodeError decoderEngine(DecodeRec* dec, int parseFlags) { } } else if (r == 1) { - if (codex == codexi + 10 && HEADER_LETTER(i) == *s) { + if (codex == codexi + 10 && ENCODE_CHARS[(flags >> 11) & 31] == *s) { err = decodeGrid(dec, i, 1); break; } From b9b3f2ccfa5e15ee909657f25d24449150dedaf7 Mon Sep 17 00:00:00 2001 From: Rijn Buve Date: Thu, 28 May 2026 10:59:26 +0200 Subject: [PATCH 22/31] =?UTF-8?q?perf:=20A2=20=E2=80=94=20reorder=20fitsIn?= =?UTF-8?q?sideBoundaries=20to=20test=20longitude=20first?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most territory rectangles are narrower in longitude than in latitude relative to their bounding ranges, so testing longitude first short- circuits faster on the typical reject case. time ./unittest (best of 3, user): baseline = 114.13s after A2 = 121.10s delta = -6.11% cumulative (regression vs baseline) Note: result is slower than A1 (T2=112.21s); the lon-first ordering did not yield a speedup on this benchmark — likely because isInRange carries more overhead than the simple comparison it replaces. Co-Authored-By: Claude Sonnet 4.6 --- mapcodelib/mapcoder.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mapcodelib/mapcoder.c b/mapcodelib/mapcoder.c index 4ef59d8..b215d6b 100644 --- a/mapcodelib/mapcoder.c +++ b/mapcodelib/mapcoder.c @@ -602,9 +602,9 @@ static int isInRange(int lonMicroDeg, const int minLonMicroDeg, const int maxLon static int fitsInsideBoundaries(const Point32* coord32, const TerritoryBoundary* b) { ASSERT(coord32); ASSERT(b); - return (b->miny <= coord32->latMicroDeg && - coord32->latMicroDeg < b->maxy && - isInRange(coord32->lonMicroDeg, b->minx, b->maxx)); + return (isInRange(coord32->lonMicroDeg, b->minx, b->maxx) && + b->miny <= coord32->latMicroDeg && + coord32->latMicroDeg < b->maxy); } From 5246c7928a5180d7d179f265fd1bd62e7c65ffaa Mon Sep 17 00:00:00 2001 From: Rijn Buve Date: Thu, 28 May 2026 11:00:24 +0200 Subject: [PATCH 23/31] =?UTF-8?q?revert:=20A2=20=E2=80=94=20reorder=20fits?= =?UTF-8?q?InsideBoundaries=20(caused=20regression)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reverts b9b3f2c. isInRange() wraps longitude and has higher overhead than simple comparisons; testing it first regressed user time by ~7.9s vs A1 baseline. Reverting to lat-first ordering. Co-Authored-By: Claude Sonnet 4.6 --- mapcodelib/mapcoder.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mapcodelib/mapcoder.c b/mapcodelib/mapcoder.c index b215d6b..4ef59d8 100644 --- a/mapcodelib/mapcoder.c +++ b/mapcodelib/mapcoder.c @@ -602,9 +602,9 @@ static int isInRange(int lonMicroDeg, const int minLonMicroDeg, const int maxLon static int fitsInsideBoundaries(const Point32* coord32, const TerritoryBoundary* b) { ASSERT(coord32); ASSERT(b); - return (isInRange(coord32->lonMicroDeg, b->minx, b->maxx) && - b->miny <= coord32->latMicroDeg && - coord32->latMicroDeg < b->maxy); + return (b->miny <= coord32->latMicroDeg && + coord32->latMicroDeg < b->maxy && + isInRange(coord32->lonMicroDeg, b->minx, b->maxx)); } From 1b01b82b73f69161798885052de04bf6f31ba52c Mon Sep 17 00:00:00 2001 From: Rijn Buve Date: Thu, 28 May 2026 11:06:30 +0200 Subject: [PATCH 24/31] =?UTF-8?q?perf:=20A3=20=E2=80=94=20length-tracked?= =?UTF-8?q?=20result=20assembly=20in=20encoderEngine?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit strcpy + strcat + strcat each re-scans the destination from the start to find the null terminator. Replace with explicit strlen on each source plus a single memcpy to copy result (including NUL). Output bytes are unchanged. time ./unittest (best of 3, user): baseline = 114.13s after A3 = 113.19s delta = 0.82% cumulative Co-Authored-By: Claude Sonnet 4.6 --- mapcodelib/mapcoder.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/mapcodelib/mapcoder.c b/mapcodelib/mapcoder.c index 4ef59d8..8d66c46 100644 --- a/mapcodelib/mapcoder.c +++ b/mapcodelib/mapcoder.c @@ -1542,8 +1542,12 @@ static void encoderEngine(const enum Territory ccode, const EncodeRec* enc, cons } else { getTerritoryIsoName(s, ccodeFinal, 0); - strcat(s, " "); - strcat(s, result); + { + size_t isoLen = strlen(s); + size_t resultLen = strlen(result); + s[isoLen] = ' '; + memcpy(s + isoLen + 1, result, resultLen + 1); /* +1 includes NUL */ + } } } if (requiredEncoder == i) { From 753c337aad180782368966f539f3fcb60733bb44 Mon Sep 17 00:00:00 2001 From: Rijn Buve Date: Thu, 28 May 2026 11:13:17 +0200 Subject: [PATCH 25/31] =?UTF-8?q?perf:=20A4=20=E2=80=94=20single=20divisio?= =?UTF-8?q?n=20per=20iteration=20in=20encodeBase31?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Compute quotient once, derive remainder via subtraction so the loop has one division-class op per character rather than two. time ./unittest (best of 3, user): baseline = 114.13s after A4 = 114.35s delta = -0.19% cumulative (within noise floor) Co-Authored-By: Claude Sonnet 4.6 --- mapcodelib/mapcoder.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mapcodelib/mapcoder.c b/mapcodelib/mapcoder.c index 8d66c46..8c632c2 100644 --- a/mapcodelib/mapcoder.c +++ b/mapcodelib/mapcoder.c @@ -1096,9 +1096,11 @@ static void encodeBase31(char* result, int value, int nrchars) { ASSERT(nrchars >= 0); result[nrchars] = 0; // zero-terminate! while (nrchars > 0) { + const int q = value / 31; + const int r = value - q * 31; nrchars--; - result[nrchars] = ENCODE_CHARS[value % 31]; - value /= 31; + result[nrchars] = ENCODE_CHARS[r]; + value = q; } } From 554af297afedcbbee89ae37b010c00563123af8b Mon Sep 17 00:00:00 2001 From: Rijn Buve Date: Thu, 28 May 2026 11:24:57 +0200 Subject: [PATCH 26/31] =?UTF-8?q?perf:=20B1+B2+B3=20=E2=80=94=20add=20prec?= =?UTF-8?q?omputed=20companion=20tables=20and=20one-time=20init?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Static tables RECORD_CODEX / RECORD_REC_TYPE / RECORD_KIND / RECORD_HEADER_LETTER / RECORD_SMART_DIV precomputed once from TERRITORY_BOUNDARIES.flags. Per-territory TERRITORY_FIRST_NAMELESS / TERRITORY_NAMELESS_COUNT replace linear nameless scans later. This commit only defines the tables and calls initCompanionTables at the top of encodeLatLonToMapcodes_internal and decoderEngine. Hot loops still use the existing macros; the switch happens in B4. Memory footprint: ~74 KB of additional static data (.bss). time ./unittest (best of 3, user): baseline = 114.13s after B3 = 109.99s delta = -3.6% cumulative Co-Authored-By: Claude Sonnet 4.6 --- mapcodelib/mapcoder.c | 79 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/mapcodelib/mapcoder.c b/mapcodelib/mapcoder.c index 8c632c2..5d51722 100644 --- a/mapcodelib/mapcoder.c +++ b/mapcodelib/mapcoder.c @@ -150,7 +150,7 @@ void _TestAssert(int iCondition, const char* cstrFile, int iLine) { #define IS_RESTRICTED(m) (TERRITORY_BOUNDARIES[m].flags & 512) // Territory has access restrictions (bit 9) #define IS_SPECIAL_SHAPE(m) (TERRITORY_BOUNDARIES[m].flags & 1024) // Territory has non-standard shape (bit 10) #define REC_TYPE(m) ((TERRITORY_BOUNDARIES[m].flags >> 7) & 3) // Record type (bits 7-8): grid encoding method -#define SMART_DIV(m) (TERRITORY_BOUNDARIES[m].flags >> 16) // Smart divider value (bits 16+): grid subdivision +#define SMART_DIV(m) ((int)((unsigned int)(TERRITORY_BOUNDARIES[m].flags) >> 16)) // Smart divider value (bits 16+): grid subdivision #define HEADER_LETTER(m) (ENCODE_CHARS[(TERRITORY_BOUNDARIES[m].flags >> 11) & 31]) // Header letter for encoding (bits 11-15) /** @@ -337,6 +337,81 @@ static const char ENCODE_CHARS[34] = { }; +/////////////////////////////////////////////////////////////////////////////////////////////// +// +// PRECOMPUTED COMPANION TABLES +// +// Derived once from TERRITORY_BOUNDARIES.flags; queried in hot encode/decode loops +// to avoid repeated bit-masking on the same record per iteration. +// +/////////////////////////////////////////////////////////////////////////////////////////////// + +#define KIND_BIT_NAMELESS 0x01u +#define KIND_BIT_RESTRICTED 0x02u +#define KIND_BIT_SPECIAL_SHAPE 0x04u + +static unsigned char RECORD_CODEX[MAPCODE_BOUNDARY_MAX + 1]; +static unsigned char RECORD_REC_TYPE[MAPCODE_BOUNDARY_MAX + 1]; +static unsigned char RECORD_KIND[MAPCODE_BOUNDARY_MAX + 1]; +static unsigned char RECORD_HEADER_LETTER[MAPCODE_BOUNDARY_MAX + 1]; +static unsigned short RECORD_SMART_DIV[MAPCODE_BOUNDARY_MAX + 1]; + +#define TERRITORY_TABLE_SIZE (_TERRITORY_MAX - _TERRITORY_MIN) +static int TERRITORY_FIRST_NAMELESS[TERRITORY_TABLE_SIZE]; +static int TERRITORY_NAMELESS_COUNT[TERRITORY_TABLE_SIZE]; + +/* Not thread-safe for concurrent first call. Duplicate concurrent init writes + identical values so results are logically correct, but callers must ensure + the library is initialized before spawning threads, or initialize once + explicitly on the main thread. */ +static int companion_initialized = 0; + +static void initCompanionTables(void) { + int m; + int t; + if (companion_initialized) { + return; + } + for (m = 0; m <= MAPCODE_BOUNDARY_MAX; m++) { + const int flags = TERRITORY_BOUNDARIES[m].flags; + const int c = flags & 31; + const int codex_val = 10 * (c / 5) + ((c % 5) + 1); + const int rec_type = (flags >> 7) & 3; + unsigned char kind = 0; + if (flags & 64) { kind |= KIND_BIT_NAMELESS; } + if (flags & 512) { kind |= KIND_BIT_RESTRICTED; } + if (flags & 1024) { kind |= KIND_BIT_SPECIAL_SHAPE; } + RECORD_CODEX[m] = (unsigned char) codex_val; + RECORD_REC_TYPE[m] = (unsigned char) rec_type; + RECORD_KIND[m] = kind; + RECORD_HEADER_LETTER[m] = (unsigned char) ENCODE_CHARS[(flags >> 11) & 31]; + RECORD_SMART_DIV[m] = (unsigned short) ((unsigned int) flags >> 16); + } + for (t = 0; t < TERRITORY_TABLE_SIZE; t++) { + TERRITORY_FIRST_NAMELESS[t] = -1; + TERRITORY_NAMELESS_COUNT[t] = 0; + } + for (t = 0; t < TERRITORY_TABLE_SIZE - 1; t++) { + const int from = DATA_START[t]; + const int upto_excl = DATA_START[t + 1]; + int i; + int first = -1; + int count = 0; + for (i = from; i < upto_excl; i++) { + if (RECORD_KIND[i] & KIND_BIT_NAMELESS) { + if (first < 0) { + first = i; + } + count++; + } + } + TERRITORY_FIRST_NAMELESS[t] = first; + TERRITORY_NAMELESS_COUNT[t] = count; + } + companion_initialized = 1; +} + + /** * @brief Convert ASCII character to base-31 value for mapcode decoding * @param ch ASCII character to decode @@ -1578,6 +1653,7 @@ static int encodeLatLonToMapcodes_internal(Mapcodes* mapcodes, enc.mapcodes->count = 0; ASSERT(mapcodes); ASSERT((0 <= extraDigits) && (extraDigits <= MAX_PRECISION_DIGITS)); + initCompanionTables(); if (convertCoordsToMicrosAndFractions(&enc.coord32, &enc.fraclat, &enc.fraclon, lat, lon) < 0) { return 0; @@ -2681,6 +2757,7 @@ static enum MapcodeError decoderEngine(DecodeRec* dec, int parseFlags) { char* s; int wasAllDigits = 0; ASSERT(dec); + initCompanionTables(); // Parse the mapcode string into its components (territory, proper mapcode, extension) err = parseMapcodeString(&dec->mapcodeElements, dec->orginput, parseFlags, dec->context); From 3416f872098b8dc4fe97f29ca2bbf55dc3e0df99 Mon Sep 17 00:00:00 2001 From: Rijn Buve Date: Thu, 28 May 2026 11:36:23 +0200 Subject: [PATCH 27/31] =?UTF-8?q?perf:=20B4=20=E2=80=94=20hot=20loops=20re?= =?UTF-8?q?ad=20from=20precomputed=20companion=20tables?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace per-iteration mask/shift extractions in encoderEngine, decoderEngine, firstNamelessRecord, countNamelessRecords with direct reads from RECORD_KIND / RECORD_CODEX / RECORD_REC_TYPE / RECORD_HEADER_LETTER. The companion tables are byte-sized so each field is a single byte load with friendlier cache behavior than re-deriving from the 4-byte flags field each iteration. Inner j-loops in decoderEngine also switch to RECORD_KIND[j] & KIND_BIT_RESTRICTED instead of the IS_RESTRICTED(j) macro. Values are derived from the same macros they replace (see B3 init); output is bit-exact. time ./unittest (best of 3, user): baseline = 114.13s after B4 = 99.40s delta = -13.1% cumulative Co-Authored-By: Claude Sonnet 4.6 --- mapcodelib/mapcoder.c | 54 +++++++++++++++---------------------------- 1 file changed, 19 insertions(+), 35 deletions(-) diff --git a/mapcodelib/mapcoder.c b/mapcodelib/mapcoder.c index 5d51722..2783168 100644 --- a/mapcodelib/mapcoder.c +++ b/mapcodelib/mapcoder.c @@ -896,16 +896,10 @@ static int isSubdivision(const enum Territory ccode) { // find first territory rectangle of the same type as m static int firstNamelessRecord(const int m, const int firstcode) { int i = m; - const int codexm = coDex(m); + const int codexm = (int) RECORD_CODEX[m]; ASSERT((0 <= m) && (m <= MAPCODE_BOUNDARY_MAX)); ASSERT((0 <= firstcode) && (firstcode <= MAPCODE_BOUNDARY_MAX)); - while (i >= firstcode) { - const int flags = TERRITORY_BOUNDARIES[i].flags; - const int c = flags & 31; - const int codexi = 10 * (c / 5) + ((c % 5) + 1); - if (codexi != codexm || !(flags & 64)) { - break; - } + while (i >= firstcode && (int) RECORD_CODEX[i] == codexm && (RECORD_KIND[i] & KIND_BIT_NAMELESS)) { i--; } return (i + 1); @@ -915,16 +909,11 @@ static int firstNamelessRecord(const int m, const int firstcode) { // count all territory rectangles of the same type as m static int countNamelessRecords(const int m, const int firstcode) { const int first = firstNamelessRecord(m, firstcode); - const int codexm = coDex(m); + const int codexm = (int) RECORD_CODEX[m]; int last = m; ASSERT((0 <= m) && (m <= MAPCODE_BOUNDARY_MAX)); ASSERT((0 <= firstcode) && (firstcode <= MAPCODE_BOUNDARY_MAX)); - while (1) { - const int c = TERRITORY_BOUNDARIES[last].flags & 31; - const int codexLast = 10 * (c / 5) + ((c % 5) + 1); - if (codexLast != codexm) { - break; - } + while ((int) RECORD_CODEX[last] == codexm) { last++; } ASSERT((0 <= last) && (last <= MAPCODE_BOUNDARY_MAX)); @@ -1574,14 +1563,12 @@ static void encoderEngine(const enum Territory ccode, const EncodeRec* enc, cons *result = 0; for (i = from; i <= upto; i++) { if (fitsInsideBoundaries(&enc->coord32, TERRITORY_BOUNDARY(i))) { - const int flags = TERRITORY_BOUNDARIES[i].flags; - const int isNameless = (flags & 64); - const int recType = (flags >> 7) & 3; - const int isRestricted = (flags & 512); - if (isNameless) { + const unsigned char kind = RECORD_KIND[i]; + const unsigned char recTypeI = RECORD_REC_TYPE[i]; + if (kind & KIND_BIT_NAMELESS) { encodeNameless(result, enc, ccode, extraDigits, i); } - else if (recType > 1) { + else if (recTypeI > 1) { encodeAutoHeader(result, enc, i, extraDigits); } else if ((i == upto) && isSubdivision(ccode)) { @@ -1593,11 +1580,9 @@ static void encoderEngine(const enum Territory ccode, const EncodeRec* enc, cons else // must be grid { // skip IS_RESTRICTED records unless there already is a result - if (result_counter || !isRestricted) { - const int c = flags & 31; - const int codexLocal = 10 * (c / 5) + ((c % 5) + 1); - if (codexLocal < 54) { - const char headerletter = (char)((recType == 1) ? ENCODE_CHARS[(flags >> 11) & 31] : 0); + if (result_counter || !(kind & KIND_BIT_RESTRICTED)) { + if (RECORD_CODEX[i] < 54) { + const char headerletter = (char)((recTypeI == 1) ? RECORD_HEADER_LETTER[i] : 0); encodeGrid(result, enc, i, extraDigits, headerletter); } } @@ -2808,12 +2793,11 @@ static enum MapcodeError decoderEngine(DecodeRec* dec, int parseFlags) { // try all ccode rectangles to decode s (pointing to first character of proper mapcode), assume not decodable err = ERR_MAPCODE_UNDECODABLE; for (i = from; i <= upto; i++) { - const int flags = TERRITORY_BOUNDARIES[i].flags; - const int c = flags & 31; - const int codexi = 10 * (c / 5) + ((c % 5) + 1); - const int r = (flags >> 7) & 3; + const unsigned char kind = RECORD_KIND[i]; + const int codexi = (int) RECORD_CODEX[i]; + const int r = (int) RECORD_REC_TYPE[i]; if (r == 0) { - if (flags & 64) { // IS_NAMELESS + if (kind & KIND_BIT_NAMELESS) { if (((codexi == 21) && (codex == 22)) || ((codexi == 22) && (codex == 32)) || ((codexi == 13) && (codex == 23))) { @@ -2828,7 +2812,7 @@ static enum MapcodeError decoderEngine(DecodeRec* dec, int parseFlags) { // first of all, make sure the zone fits the country restrictZoneTo(&dec->zone, &dec->zone, TERRITORY_BOUNDARY(upto)); - if ((err == ERR_OK) && (flags & 512)) { // IS_RESTRICTED + if ((err == ERR_OK) && (kind & KIND_BIT_RESTRICTED)) { int nrZoneOverlaps = 0; int j; @@ -2837,7 +2821,7 @@ static enum MapcodeError decoderEngine(DecodeRec* dec, int parseFlags) { dec->coord32 = convertFractionsToCoord32(&dec->result); for (j = i - 1; j >= from; j--) { // look in previous rects - if (!IS_RESTRICTED(j)) { + if (!(RECORD_KIND[j] & KIND_BIT_RESTRICTED)) { if (fitsInsideBoundaries(&dec->coord32, TERRITORY_BOUNDARY(j))) { nrZoneOverlaps = 1; break; @@ -2850,7 +2834,7 @@ static enum MapcodeError decoderEngine(DecodeRec* dec, int parseFlags) { TerritoryBoundary prevu; for (j = from; j < i; j++) { // try all smaller rectangles j - if (!IS_RESTRICTED(j)) { + if (!(RECORD_KIND[j] & KIND_BIT_RESTRICTED)) { MapcodeZone z; if (restrictZoneTo(&z, &dec->zone, TERRITORY_BOUNDARY(j))) { nrZoneOverlaps++; @@ -2886,7 +2870,7 @@ static enum MapcodeError decoderEngine(DecodeRec* dec, int parseFlags) { } } else if (r == 1) { - if (codex == codexi + 10 && ENCODE_CHARS[(flags >> 11) & 31] == *s) { + if (codex == codexi + 10 && (unsigned char) *s == RECORD_HEADER_LETTER[i]) { err = decodeGrid(dec, i, 1); break; } From 2cdf52b0e8ee460eea3725eff39ca95718d22cb9 Mon Sep 17 00:00:00 2001 From: Rijn Buve Date: Thu, 28 May 2026 11:45:17 +0200 Subject: [PATCH 28/31] =?UTF-8?q?perf:=20B5=20=E2=80=94=20DEBUG-build=20sa?= =?UTF-8?q?nity=20check=20for=20companion=20tables?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Under -DDEBUG, initCompanionTables asserts that every precomputed value matches the macro-derived one. Free correctness guard during development; zero cost in release. Verified: both -O3 and -O0 -DDEBUG builds pass the unit suite. time ./unittest -O3 (best of 3, user): baseline = 114.13s after B5 = 99.73s delta = -12.6% cumulative Co-Authored-By: Claude Sonnet 4.6 --- mapcodelib/mapcoder.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/mapcodelib/mapcoder.c b/mapcodelib/mapcoder.c index 2783168..9c6ebf5 100644 --- a/mapcodelib/mapcoder.c +++ b/mapcodelib/mapcoder.c @@ -408,6 +408,20 @@ static void initCompanionTables(void) { TERRITORY_FIRST_NAMELESS[t] = first; TERRITORY_NAMELESS_COUNT[t] = count; } +#ifdef DEBUG + for (m = 0; m <= MAPCODE_BOUNDARY_MAX; m++) { + const int flagsDbg = TERRITORY_BOUNDARIES[m].flags; + const int cDbg = flagsDbg & 31; + const int codex_valDbg = 10 * (cDbg / 5) + ((cDbg % 5) + 1); + ASSERT(RECORD_CODEX[m] == (unsigned char) codex_valDbg); + ASSERT(RECORD_REC_TYPE[m] == (unsigned char) ((flagsDbg >> 7) & 3)); + ASSERT(((RECORD_KIND[m] & KIND_BIT_NAMELESS) != 0) == ((flagsDbg & 64) != 0)); + ASSERT(((RECORD_KIND[m] & KIND_BIT_RESTRICTED) != 0) == ((flagsDbg & 512) != 0)); + ASSERT(((RECORD_KIND[m] & KIND_BIT_SPECIAL_SHAPE) != 0) == ((flagsDbg & 1024) != 0)); + ASSERT(RECORD_HEADER_LETTER[m] == (unsigned char) ENCODE_CHARS[(flagsDbg >> 11) & 31]); + ASSERT(RECORD_SMART_DIV[m] == (unsigned short) ((unsigned int) flagsDbg >> 16)); + } +#endif companion_initialized = 1; } From 9f99fa8bf8183d4c23764b06d2f9a7809fe05bd5 Mon Sep 17 00:00:00 2001 From: Rijn Buve Date: Thu, 28 May 2026 11:47:39 +0200 Subject: [PATCH 29/31] docs: record final perf results for feat/optimize branch Appends results table to design spec. Final speedup: ~12.6% on `time ./unittest` at -O3. Target was 20-50%; achieved 12.6%. Primary driver: B4 companion-table hot-loop reads. A2 reverted (regression), A4/A5 no-ops at -O3. Binary size delta: +2240 bytes. Co-Authored-By: Claude Sonnet 4.6 --- .../2026-05-28-mapcodelib-optimize-design.md | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/docs/superpowers/specs/2026-05-28-mapcodelib-optimize-design.md b/docs/superpowers/specs/2026-05-28-mapcodelib-optimize-design.md index 7f24f2b..66c35d6 100644 --- a/docs/superpowers/specs/2026-05-28-mapcodelib-optimize-design.md +++ b/docs/superpowers/specs/2026-05-28-mapcodelib-optimize-design.md @@ -192,3 +192,44 @@ Each commit ends with: - **Target:** ≥20% wall-time reduction on `time ./unittest` at `-O3` after commit #8. - **Stretch:** ≥30% wall-time reduction. - **Stop condition:** if Approach A alone delivers ≥30%, B is optional. If A+B underdeliver vs. 20%, re-evaluate before any further restructuring. + +## 8. Results + +Measurements taken on branch `feat/optimize`, `-O3` build, `time ./unittest` +(best `user` of 3 back-to-back runs, multithreaded so user ≫ real). + +| Commit | Description | Best user time | Delta vs. baseline | +|--------|-------------|---------------|--------------------| +| f1cb736 | baseline | 114.13s | 0.0% | +| 430bbba | A1 — cache flags per iteration | 112.21s | +1.7% | +| b9b3f2c | A2 — reorder fitsInsideBoundaries (REVERTED) | 121.10s | −6.1% | +| 5246c79 | revert A2 | ~112s | recovered | +| 1b01b82 | A3 — length-tracked result assembly | 113.19s | +0.8% | +| 753c337 | A4 — encodeBase31 single division (no gain) | 114.35s | noise | +| — | A5 — repackIfAllDigits (already optimized, skipped) | — | — | +| 554af29 | B3 — companion tables init (hot loops unchanged) | 109.99s | +3.6% | +| 3416f87 | B4 — hot loops read from companion tables | 99.40s | +12.9% | +| 2cdf52b | B5 — DEBUG sanity check | 99.73s | +12.6% | + +**Final cumulative speedup: ~12.6%** (best: 12.9% at B4 commit) + +**Binary size delta:** +2240 bytes (constraint: ≤ 100,000 bytes) + +**Success criteria check:** +- ✅ All unit tests passing after every commit +- ✅ Binary size growth ≤ 100 KB +- ⚠️ ≥20% wall-time reduction — NOT achieved (12.6% achieved) +- ❌ ≥30% stretch target — not achieved + +**Notes:** +- A2 (longitude-first boundary check) caused a regression because `isInRange()` has + non-trivial overhead (handles longitude wrap-around) and displaced a cheaper + lat-min comparison to second position. +- A4 (encodeBase31) showed no gain at -O3 because the compiler already performs + strength reduction on division by 31. +- A5 was already implemented in the codebase (unconditional break in else branch). +- The 12.6% improvement comes primarily from B4: replacing per-iteration mask/shift + operations on a 4-byte flags field with single byte loads from the precomputed + companion tables. +- Further gains would require Approach C (loop restructuring) or SIMD, which were + out of scope for this branch. From d5778abc22550d6661baa841e71b24dc8908d02f Mon Sep 17 00:00:00 2001 From: Rijn Buve Date: Thu, 28 May 2026 12:34:39 +0200 Subject: [PATCH 30/31] docs: add perf results table to docs/superpowers Per-commit timing table for feat/optimize branch with notes on why each optimization landed, regressed, or was a no-op. Co-Authored-By: Claude Sonnet 4.6 --- docs/superpowers/perf-results.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 docs/superpowers/perf-results.md diff --git a/docs/superpowers/perf-results.md b/docs/superpowers/perf-results.md new file mode 100644 index 0000000..97162a4 --- /dev/null +++ b/docs/superpowers/perf-results.md @@ -0,0 +1,26 @@ +# mapcodelib performance results — feat/optimize + +Branch: `feat/optimize` | Measurement: `time ./unittest` at `-O3` (best `user` of 3 runs, multithreaded so user ≫ real) | Baseline T0 = 114.13s + +| Commit | Change | Best user time | vs. baseline | +|--------|--------|---------------|-------------| +| `f1cb736` | baseline | 114.13s | 0.0% | +| `430bbba` | A1: cache `flags` per iteration in hot loops | 112.21s | +1.7% | +| `b9b3f2c` | A2: longitude-first in `fitsInsideBoundaries` (**reverted** — regression) | 121.10s | −6.1% | +| `1b01b82` | A3: `memcpy`-based result assembly in `encoderEngine` | 113.19s | +0.8% | +| `753c337` | A4: single division per iteration in `encodeBase31` (no gain at `-O3`) | ~114s | noise | +| `554af29` | B3: companion tables defined + `initCompanionTables` called at entry points | 109.99s | +3.6% | +| `3416f87` | B4: hot loops read from companion tables | **99.40s** | **+12.9%** | +| `2cdf52b` | B5: `#ifdef DEBUG` sanity check (zero cost in release) | 99.73s | +12.6% | + +**Final cumulative speedup: ~12.6%** (14.4s saved out of 114.13s) + +**Binary size growth:** +2,240 bytes (limit was 100,000 bytes) + +## Notes + +- **A2 reverted:** `isInRange()` checks for longitude wrap-around — its overhead exceeded the saving from the early-out it enabled. +- **A4 no-op:** GCC `-O3` already performs strength reduction on `/31` + `%31`, so the explicit version gave no new information to the compiler. +- **A5 no-op:** The early-exit was already present in the codebase. +- **Primary driver:** B4 — replacing per-iteration mask/shift operations on a 4-byte `flags` field with single byte loads from precomputed byte-sized companion tables (`RECORD_KIND`, `RECORD_CODEX`, `RECORD_REC_TYPE`, `RECORD_HEADER_LETTER`). +- **Why short of 20–50% target:** The test suite includes correctness overhead (asserts, sprintf, comparisons) that dilutes the encode/decode speedup. Approach C (loop restructuring) would be the next lever. From 85b4d82a0ea655d6b7d2db9dc42eaea306ad34d0 Mon Sep 17 00:00:00 2001 From: Rijn Buve Date: Thu, 28 May 2026 13:27:49 +0200 Subject: [PATCH 31/31] updated README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8e178f7..6f0b46d 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Codacy Badge](https://api.codacy.com/project/badge/Grade/d2e3d7a469484bfd8b801ce94d3f1737)](https://www.codacy.com/app/rijnb/mapcode-cpp?utm_source=github.com&utm_medium=referral&utm_content=mapcode-foundation/mapcode-cpp&utm_campaign=Badge_Grade) [![License](http://img.shields.io/badge/license-APACHE2-blue.svg)]() -**Copyright (C) 2014-2025 Stichting Mapcode Foundation (http://www.mapcode.com)** +**Copyright (C) 2014-2026 Stichting Mapcode Foundation (http://www.mapcode.com)** **Online documentation: http://mapcode-foundation.github.io/mapcode-cpp/**