MapsWithMe_GenShortShowMapUrl

This commit is contained in:
Yury Melnichek 2013-03-20 23:28:22 +01:00 committed by Alex Zolotarev
parent 3faba8bfec
commit 8446dd0682
2 changed files with 282 additions and 3 deletions

View file

@ -1,6 +1,8 @@
#include "api-client.h"
#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
// Max number of base64 bytes to encode a geo point.
#define MAPSWITHME_MAX_POINT_BYTES 10
@ -70,10 +72,113 @@ void MapsWithMe_LatLonToString(double lat, double lon, char * s, int nBytes)
}
}
int MapsWithMe_GenShortShowMapUrl(double lat, double lon, double zoomLevel, char const * name, char * buf, int bufSize)
// Do special URL Encoding:
// URL restricted / unsafe / unwise characters are %-encoded.
// See rfc3986, rfc1738, rfc2396.
// ' ' is replaced with '_'
// '_' is replaces with %-encoded space, i.e. %20
int MapsWithMe_UrlEncodeString(char const * s, int size, char ** res)
{
*res = malloc(size * 3 + 1);
char * out = *res;
for (int i = 0; i < size; ++i)
{
unsigned char c = (unsigned char)(s[i]);
switch (c)
{
case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07:
case 0x08: case 0x09: case 0x0A: case 0x0B: case 0x0C: case 0x0D: case 0x0E: case 0x0F:
case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17:
case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1E: case 0x1F:
case 0x7F:
case '<':
case '>':
case '#':
case '%':
case '"':
case '!':
case '*':
case '\'':
case '(':
case ')':
case ';':
case ':':
case '@':
case '&':
case '=':
case '+':
case '$':
case ',':
case '/':
case '?':
case '[':
case ']':
case '{':
case '}':
case '|':
case '^':
case '`':
*(out++) = '%';
*(out++) = "0123456789ABCDEF"[c >> 4];
*(out++) = "0123456789ABCDEF"[c & 15];
break;
case ' ':
*(out++) = '_';
break;
case '_':
*(out++) = '%'; *(out++) = '2'; *(out++) = '0';
break;
default:
*(out++) = s[i];
}
}
*(out++) = 0;
return out - *res - 1;
}
void MapsWithMe_AppendString(char * buf, int bufSize, int * bytesAppended, char const * s, int size)
{
int const bytesAvailable = bufSize - *bytesAppended;
if (bytesAvailable > 0)
memcpy(buf + *bytesAppended, s, size < bytesAvailable ? size : bytesAvailable);
*bytesAppended += size;
}
int MapsWithMe_GenShortShowMapUrl(double lat, double lon, double zoom, char const * name, char * buf, int bufSize)
{
// @TODO: Implement MapsWithMe_GenShortShowMapUrl().
// @TODO: Escape URL-unfriendly characters: ! * ' ( ) ; : @ & = + $ , / ? % # [ ]
return 0;
// URL format:
//
// +------------------ 1 byte: zoom level
// |+-------+--------- 9 bytes: lat,lon
// || | +--+---- Variable number of bytes: point name
// || | | |
// ge0://ZCoordba64/Name
int fullUrlSize = 0;
char urlPrefix[] = "ge0://ZCoord6789";
int const zoomI = (zoom <= 4 ? 0 : (zoom >= 19.75 ? 63 : (int) ((zoom - 4) * 4)));
urlPrefix[6] = MapsWithMe_Base64Char(zoomI);
MapsWithMe_LatLonToString(lat, lon, urlPrefix + 7, 9);
MapsWithMe_AppendString(buf, bufSize, &fullUrlSize, urlPrefix, 16);
if (name != 0 && name[0] != 0)
{
MapsWithMe_AppendString(buf, bufSize, &fullUrlSize, "/", 1);
char * encName;
int const encNameSize = MapsWithMe_UrlEncodeString(name, strlen(name), &encName);
MapsWithMe_AppendString(buf, bufSize, &fullUrlSize, encName, encNameSize);
free(encName);
}
return fullUrlSize;
}

View file

@ -249,5 +249,179 @@ FCT_BGN()
fct_chk((max2 - min2) * 1.0 / max2 < 0.05);
}
FCT_QTEST_END();
FCT_QTEST_BGN(MapsWithMe_GenShortShowMapUrl_SmokeTest)
{
char buf[100] = {0};
int res = MapsWithMe_GenShortShowMapUrl(0, 0, 19, "Name", buf, 100);
fct_chk_eq_str("ge0://8wAAAAAAAA/Name", buf);
fct_chk_eq_int(21, res);
}
FCT_QTEST_END();
FCT_QTEST_BGN(MapsWithMe_GenShortShowMapUrl_NameIsNull)
{
char buf[100] = {0};
int res = MapsWithMe_GenShortShowMapUrl(0, 0, 19, 0, buf, 100);
fct_chk_eq_str("ge0://8wAAAAAAAA", buf);
fct_chk_eq_int(16, res);
}
FCT_QTEST_END();
FCT_QTEST_BGN(MapsWithMe_GenShortShowMapUrl_NameIsEmpty)
{
char buf[100] = {0};
int res = MapsWithMe_GenShortShowMapUrl(0, 0, 19, "", buf, 100);
fct_chk_eq_str("ge0://8wAAAAAAAA", buf);
fct_chk_eq_int(16, res);
}
FCT_QTEST_END();
FCT_QTEST_BGN(MapsWithMe_GenShortShowMapUrl_ZoomVerySmall)
{
char buf[100] = {0};
int res = MapsWithMe_GenShortShowMapUrl(0, 0, 2, "Name", buf, 100);
fct_chk_eq_str("ge0://AwAAAAAAAA/Name", buf);
fct_chk_eq_int(21, res);
}
FCT_QTEST_END();
FCT_QTEST_BGN(MapsWithMe_GenShortShowMapUrl_ZoomNegative)
{
char buf[100] = {0};
int res = MapsWithMe_GenShortShowMapUrl(0, 0, -5, "Name", buf, 100);
fct_chk_eq_str("ge0://AwAAAAAAAA/Name", buf);
fct_chk_eq_int(21, res);
}
FCT_QTEST_END();
FCT_QTEST_BGN(MapsWithMe_GenShortShowMapUrl_ZoomLarge)
{
char buf[100] = {0};
int res = MapsWithMe_GenShortShowMapUrl(0, 0, 20, "Name", buf, 100);
fct_chk_eq_str("ge0://_wAAAAAAAA/Name", buf);
fct_chk_eq_int(21, res);
}
FCT_QTEST_END();
FCT_QTEST_BGN(MapsWithMe_GenShortShowMapUrl_ZoomVeryLarge)
{
char buf[100] = {0};
int res = MapsWithMe_GenShortShowMapUrl(0, 0, 2000000000, "Name", buf, 100);
fct_chk_eq_str("ge0://_wAAAAAAAA/Name", buf);
fct_chk_eq_int(21, res);
}
FCT_QTEST_END();
FCT_QTEST_BGN(MapsWithMe_GenShortShowMapUrl_FractionalZoom)
{
char buf[100] = {0};
int res = MapsWithMe_GenShortShowMapUrl(0, 0, 8.25, "Name", buf, 100);
fct_chk_eq_str("ge0://RwAAAAAAAA/Name", buf);
fct_chk_eq_int(21, res);
}
FCT_QTEST_END();
FCT_QTEST_BGN(MapsWithMe_GenShortShowMapUrl_FractionalZoomRoundsDown)
{
char buf[100] = {0};
int res = MapsWithMe_GenShortShowMapUrl(0, 0, 8.499, "Name", buf, 100);
fct_chk_eq_str("ge0://RwAAAAAAAA/Name", buf);
fct_chk_eq_int(21, res);
}
FCT_QTEST_END();
FCT_QTEST_BGN(MapsWithMe_GenShortShowMapUrl_FractionalZoomNextStep)
{
char buf[100] = {0};
int res = MapsWithMe_GenShortShowMapUrl(0, 0, 8.5, "Name", buf, 100);
fct_chk_eq_str("ge0://SwAAAAAAAA/Name", buf);
fct_chk_eq_int(21, res);
}
FCT_QTEST_END();
FCT_QTEST_BGN(MapsWithMe_GenShortShowMapUrl_SpaceIsReplacedWithUnderscore)
{
char buf[100] = {0};
int res = MapsWithMe_GenShortShowMapUrl(0, 0, 19, "Hello World", buf, 100);
fct_chk_eq_str("ge0://8wAAAAAAAA/Hello_World", buf);
fct_chk_eq_int(28, res);
}
FCT_QTEST_END();
FCT_QTEST_BGN(MapsWithMe_GenShortShowMapUrl_NamesAreEscaped)
{
char buf[100] = {0};
int res = MapsWithMe_GenShortShowMapUrl(0, 0, 19, "'Hello,World!%$", buf, 100);
fct_chk_eq_str("ge0://8wAAAAAAAA/%27Hello%2CWorld%21%25%24", buf);
fct_chk_eq_int(42, res);
}
FCT_QTEST_END();
FCT_QTEST_BGN(MapsWithMe_GenShortShowMapUrl_UnderscoreIsReplacedWith%20)
{
char buf[100] = {0};
int res = MapsWithMe_GenShortShowMapUrl(0, 0, 19, "Hello_World", buf, 100);
fct_chk_eq_str("ge0://8wAAAAAAAA/Hello%20World", buf);
fct_chk_eq_int(30, res);
}
FCT_QTEST_END();
FCT_QTEST_BGN(MapsWithMe_GenShortShowMapUrl_ControlCharsAreEscaped)
{
char buf[100] = {0};
int res = MapsWithMe_GenShortShowMapUrl(0, 0, 19, "Hello\tWorld\n", buf, 100);
fct_chk_eq_str("ge0://8wAAAAAAAA/Hello%09World%0A", buf);
fct_chk_eq_int(33, res);
}
FCT_QTEST_END();
FCT_QTEST_BGN(MapsWithMe_GenShortShowMapUrl_BufferNullAndEmpty)
{
int res = MapsWithMe_GenShortShowMapUrl(0, 0, 19, "Name", 0, 0);
fct_chk_eq_int(21, res);
}
FCT_QTEST_END();
FCT_QTEST_BGN(MapsWithMe_GenShortShowMapUrl_BufferNotNullAndEmpty)
{
char buf[100] = {0};
int res = MapsWithMe_GenShortShowMapUrl(0, 0, 19, "Name", buf, 0);
fct_chk_eq_int(21, res);
}
FCT_QTEST_END();
FCT_QTEST_BGN(MapsWithMe_GenShortShowMapUrl_TerminatingNullIsNotWritten)
{
char buf[] = "xxxxxxxxxxxxxxxxxxxxxxxxxxx";
int res = MapsWithMe_GenShortShowMapUrl(0, 0, 19, "Name", buf, 27);
fct_chk_eq_str("ge0://8wAAAAAAAA/Namexxxxxx", buf);
fct_chk_eq_int(21, res);
}
FCT_QTEST_END();
FCT_QTEST_BGN(MapsWithMe_GenShortShowMapUrl_BufferIs1Byte)
{
char buf;
int res = MapsWithMe_GenShortShowMapUrl(0, 0, 19, "Name", &buf, 1);
fct_chk_eq_int('g', buf);
fct_chk_eq_int(21, res);
}
FCT_QTEST_END();
FCT_QTEST_BGN(MapsWithMe_GenShortShowMapUrl_BufferTooSmall)
{
for (int bufSize = 1; bufSize <= 21; ++bufSize)
{
char buf[100] = {0};
int res = MapsWithMe_GenShortShowMapUrl(0, 0, 19, "Name", buf, bufSize);
char expected[] = "ge0://8wAAAAAAAA/Name";
expected[bufSize] = 0;
fct_chk_eq_str(expected, buf);
fct_chk_eq_int(21, res);
}
}
FCT_QTEST_END();
}
FCT_END();