473,394 Members | 1,829 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,394 software developers and data experts.

LastIndexOf similar C++ code?

Hi,

I'm 'improving' CComBSTR (yes, I do still program unmanaged code in addition
to C# ) to contain features, not found in it.

Does anybody have good code which maches LastIndexOf()?

If this is ready, I will publish the full CComBSTR class on my weblog.

int LastIndexOf(const wchar_t *src, const int startIndex = -1, const bool
caseInsensitive = false) throw()

{

// use _wcsnicmp and wcsncmp instead?

int result = -1;
if (m_str != NULL && src != NULL)

{

SIZE_T compLen = wcslen(src);

SIZE_T maxLen = Length();

SIZE_T startIdx = startIndex == 0 ? maxLen : startIndex;

SIZE_T maxScan = maxLen - startIndex;

BSTR temp, findThis;

if (caseInsensitive)

{
temp = _wcslwr(Substring(startIndex));

findThis = _wcslwr(::SysAllocString(src));
}

else

{

temp = (PWSTR)m_str;

findThis = (PWSTR)src;

}

PWSTR found = 0;//wcsrchr(temp, findThis);

if (found != NULL)

result = found - temp;

if (caseInsensitive)

{

::SysFreeString(findThis);

::SysFreeString(temp);

}

}

return result;

}

Nov 17 '05 #1
1 4445


"Egbert Nierop (MVP for IIS)" <eg***********@nospam.invalid> wrote in
message news:OC****************@tk2msftngp13.phx.gbl...
Hi,

I'm 'improving' CComBSTR (yes, I do still program unmanaged code in
addition to C# ) to contain features, not found in it.

Does anybody have good code which maches LastIndexOf()?


Ok, ok, I did it myself, however it could be improved a little, still...

These improvements also has been optimized -not- to reallocate the string so
often as the original does with Append

/////////////////////////////////////////////////////////////////////////////
// CComBSTR

class CComBSTR
{
public:
BSTR m_str;
CComBSTR() throw()
{
m_str = NULL;
}
CComBSTR(int nSize)
{
//if (nSize == 0) //BUG it should be possible to assign a L"" string
// m_str = NULL;
m_str = ::SysAllocStringLen(NULL, nSize);
if (m_str == NULL)
AtlThrow(E_OUTOFMEMORY);

}
CComBSTR(int nSize, LPCOLESTR sz)
{
if (nSize == 0)
m_str = NULL;
else
{
m_str = ::SysAllocStringLen(sz, nSize);
if (m_str == NULL)
AtlThrow(E_OUTOFMEMORY);
}
}
CComBSTR(LPCOLESTR pSrc)
{
if (pSrc == NULL)
m_str = NULL;
else
{
m_str = ::SysAllocString(pSrc);
if (m_str == NULL)
AtlThrow(E_OUTOFMEMORY);
}
}
CComBSTR(const CComBSTR& src)
{
m_str = src.Copy();
if (!!src && m_str == NULL)
AtlThrow(E_OUTOFMEMORY);

}
CComBSTR(REFGUID guid)
{
OLECHAR szGUID[64];
::StringFromGUID2(guid, szGUID, 64);
m_str = ::SysAllocString(szGUID);
if (m_str == NULL)
AtlThrow(E_OUTOFMEMORY);
}

CComBSTR& operator=(const CComBSTR& src)
{
if (m_str != src.m_str)
{
if (::SysReAllocStringLen(&m_str, src, src.Length()) == FALSE)
AtlThrow(E_OUTOFMEMORY);
}
return *this;
}

CComBSTR& operator=(LPCOLESTR pSrc)
{
if (pSrc != m_str)
{
if (pSrc != NULL)
{
if (::SysReAllocString(&m_str, pSrc) == FALSE)
AtlThrow(E_OUTOFMEMORY);
}
else
{
::SysFreeString(m_str);
m_str = NULL;
}
}
return *this;
}

~CComBSTR() throw()
{
::SysFreeString(m_str);
}
unsigned int Length() const throw()
{
return (m_str == NULL)? 0 : SysStringLen(m_str);
}
unsigned int ByteLength() const throw()
{
return (m_str == NULL)? 0 : SysStringByteLen(m_str);
}
operator BSTR() const throw()
{
return m_str;
}
BSTR* operator&() throw()
{
return &m_str;
}
BSTR Copy() const throw()
{
if (m_str == NULL)
return NULL;
return ::SysAllocStringByteLen((char*)m_str, ::SysStringByteLen(m_str));
}
HRESULT CopyTo(BSTR* pbstr) throw()
{
ATLASSERT(pbstr != NULL);
if (pbstr == NULL)
return E_POINTER;
*pbstr = Copy();
if ((*pbstr == NULL) && (m_str != NULL))
return E_OUTOFMEMORY;
return S_OK;
}

/* added by may 2005 e.n. needs #include 'wchar.h'*/
HRESULT __cdecl Format(PCWSTR pszFormat, ...) throw()
{
va_list args;
HRESULT hr = S_OK;

va_start( args, pszFormat );

int len = _vscwprintf( pszFormat, args );
if (::SysReAllocStringLen(&m_str, NULL, len) == FALSE)
hr = E_OUTOFMEMORY;
else
vswprintf( m_str, (SIZE_T)Length(), pszFormat, args );
va_end(args);

return hr;
}

BSTR Substring(const int startIndex) throw()
{
int maxIdx = Length();
if (m_str != NULL && startIndex >= 0 && startIndex <= maxIdx)
{
return ::SysAllocStringLen(m_str + startIndex, maxIdx - startIndex);
}
else
return NULL;
}

BSTR Substring(const int startIndex, int length) throw()
{
int maxIdx = Length();
if (length < 0) length = 0;
if (startIndex + length > maxIdx)
length = maxIdx - startIndex;
if (m_str != NULL && startIndex > 0 && startIndex <= maxIdx)
{
return ::SysAllocStringLen(m_str + startIndex, length);
}
else
return NULL;
}
int LastIndexOf(const wchar_t *src, const unsigned int startIndex = 0,
const bool caseInsensitive = false) throw()
{
// use _wcsnicmp and wcsncmp
int result = -1;

if (m_str != NULL && src != NULL)
{
int compLen = ocslen(src);
int maxLen = Length();

for(int x = maxLen - compLen; x >= 0; x--)
{
if (
(caseInsensitive ? _wcsnicmp(m_str + x, src, compLen) : wcsncmp(m_str
+ x, src, compLen)
) == 0)
{
result = x;
break;
}
}

}
return result;
}

int IndexOf(const wchar_t src, const unsigned int startIndex = 0, const
bool caseInsensitive = false) throw()
{
int result = -1;
if (m_str != NULL)
{
unsigned int maxLen = Length();
unsigned int startIdx = startIndex > maxLen ? maxLen : startIndex;

unsigned int maxScan = maxLen - startIndex;

BSTR temp;
wchar_t src2;
if (caseInsensitive)
{
wchar_t tol[2] = {src, 0};
_wcslwr(tol);
src2 = tol[0];

temp = _wcslwr(Substring((UINT)startIdx));
}
else
{
temp = m_str;
src2 = src;
}

wchar_t *found = wmemchr(temp, src2, maxScan);

if (found != NULL)
result = PtrToInt(found - temp);

if (caseInsensitive)
::SysFreeString(temp);
}
return result;
}

/*
* Addded by E.N.
*/
int IndexOf(const PWSTR src, const unsigned int startIndex = 0, const bool
caseInsensitive = false) throw()
{
int result = -1;

if (src != NULL && m_str != NULL)
{
unsigned int maxLen = Length();
unsigned int startIdx = startIndex > maxLen ? maxLen : startIndex;
BSTR temp, findThis;
if (caseInsensitive)
{
temp = _wcslwr(Substring(startIdx));
findThis = _wcslwr(::SysAllocString(src));
}
else
{
temp = m_str + startIdx;
findThis = src;
}
wchar_t *found = wcsstr(temp, findThis);
if (found != NULL)
result = PtrToInt(found - temp);

if (caseInsensitive)
{
::SysFreeString(temp);
::SysFreeString(findThis);
}
}
return result;

}

// copy BSTR to VARIANT
HRESULT CopyTo(VARIANT *pvarDest) throw()
{
ATLASSERT(pvarDest != NULL);
HRESULT hRes = E_POINTER;
if (pvarDest != NULL)
{
pvarDest->vt = VT_BSTR;
pvarDest->bstrVal = Copy();
if (pvarDest->bstrVal == NULL && m_str != NULL)
hRes = E_OUTOFMEMORY;
else
hRes = S_OK;
}
return hRes;
}
void Attach(BSTR src) throw()
{
if (m_str != src)
{
::SysFreeString(m_str);
m_str = src;
}
}
BSTR Detach() throw()
{
BSTR s = m_str;
m_str = NULL;
return s;
}
void Empty() throw()
{
::SysFreeString(m_str);
m_str = NULL;
}
bool operator!() const throw()
{
return (m_str == NULL);
}
HRESULT Append(const CComBSTR& bstrSrc) throw()
{
return AppendBSTR(bstrSrc.m_str);
}
HRESULT Append(LPCOLESTR lpsz) throw()
{
return Append(lpsz, UINT(ocslen(lpsz)));
}
// a BSTR is just a LPCOLESTR so we need a special version to signify
// that we are appending a BSTR
HRESULT AppendBSTR(BSTR p) throw()
{
return Append((LPCOLESTR)p, ::SysStringLen(p));
}
HRESULT Append(LPCOLESTR lpsz, int nLen) throw()
{
if (lpsz == NULL || (m_str != NULL && nLen == 0))
return S_OK;
int n1 = Length();
if ( ::SysReAllocStringLen(&m_str, NULL, n1+nLen) == FALSE)
return E_OUTOFMEMORY;
memcpy(m_str+n1, lpsz, nLen*sizeof(OLECHAR));
return S_OK;
}
HRESULT Append(char ch) throw()
{
OLECHAR chO = ch;

return( Append( &chO, 1 ) );
}
HRESULT Append(wchar_t ch) throw()
{
return( Append( &ch, 1 ) );
}
HRESULT AppendBytes(const char* lpsz, int nLen) throw()
{
if (lpsz == NULL || nLen == 0)
return S_OK;
int n1 = ByteLength();
BSTR b;
b = ::SysAllocStringByteLen(NULL, n1+nLen);
if (b == NULL)
return E_OUTOFMEMORY;
memcpy(b, m_str, n1);
memcpy(((char*)b)+n1, lpsz, nLen);
*((OLECHAR*)(((char*)b)+n1+nLen)) = NULL;
SysFreeString(m_str);
m_str = b;
return S_OK;
}
HRESULT AssignBSTR(const BSTR bstrSrc) throw()
{
HRESULT hr = S_OK;
if (m_str != bstrSrc)
{

if (bstrSrc != NULL)
{
if (::SysReAllocStringLen(&m_str, bstrSrc, ::SysStringLen(bstrSrc)) ==
FALSE)
hr = E_OUTOFMEMORY;
}
else
m_str = NULL;
}

return hr;
}
HRESULT ToLower() throw()
{
if (m_str != NULL)
{
#ifdef _UNICODE
// Convert in place
CharLowerBuff(m_str, Length());
#else
// Cannot use conversion macros due to possible embedded NULLs
UINT _acp = _AtlGetConversionACP();
int _convert = WideCharToMultiByte(_acp, 0, m_str, Length(), NULL, 0,
NULL, NULL);
CTempBuffer<char> pszA;
ATLTRY(pszA.Allocate(_convert));
if (pszA == NULL)
return E_OUTOFMEMORY;

int nRet = WideCharToMultiByte(_acp, 0, m_str, Length(), pszA, _convert,
NULL, NULL);
if (nRet == 0)
{
ATLASSERT(0);
return AtlHresultFromLastError();
}

CharLowerBuff(pszA, nRet);

_convert = MultiByteToWideChar(_acp, 0, pszA, nRet, NULL, 0);

CTempBuffer<WCHAR> pszW;
ATLTRY(pszW.Allocate(_convert));
if (pszW == NULL)
return E_OUTOFMEMORY;

nRet = MultiByteToWideChar(_acp, 0, pszA, nRet, pszW, _convert);
if (nRet == 0)
{
ATLASSERT(0);
return AtlHresultFromLastError();
}

if (::SysReAllocStringLen(&m_str, pszW, nRet) == FALSE)
return E_OUTOFMEMORY;
#endif
}
return S_OK;
}
HRESULT ToUpper() throw()
{
if (m_str != NULL)
{
#ifdef _UNICODE
// Convert in place
CharUpperBuff(m_str, Length());
#else
// Cannot use conversion macros due to possible embedded NULLs
UINT _acp = _AtlGetConversionACP();
int _convert = WideCharToMultiByte(_acp, 0, m_str, Length(), NULL, 0,
NULL, NULL);
CTempBuffer<char> pszA;
ATLTRY(pszA.Allocate(_convert));
if (pszA == NULL)
return E_OUTOFMEMORY;

int nRet = WideCharToMultiByte(_acp, 0, m_str, Length(), pszA, _convert,
NULL, NULL);
if (nRet == 0)
{
ATLASSERT(0);
return AtlHresultFromLastError();
}

CharUpperBuff(pszA, nRet);

_convert = MultiByteToWideChar(_acp, 0, pszA, nRet, NULL, 0);

CTempBuffer<WCHAR> pszW;
ATLTRY(pszW.Allocate(_convert));
if (pszW == NULL)
return E_OUTOFMEMORY;

nRet = MultiByteToWideChar(_acp, 0, pszA, nRet, pszW, _convert);
if (nRet == 0)
{
ATLASSERT(0);
return AtlHresultFromLastError();
}

if (::SysReAllocStringLen(&m_str, pszW, nRet) == FALSE);
return E_OUTOFMEMORY;

#endif
}
return S_OK;
}
bool LoadString(HINSTANCE hInst, UINT nID) throw()
{
::SysFreeString(m_str);
m_str = NULL;
return LoadStringResource(hInst, nID, m_str);
}
bool LoadString(UINT nID) throw()
{
::SysFreeString(m_str);
m_str = NULL;
return LoadStringResource(nID, m_str);
}

CComBSTR& operator+=(const CComBSTR& bstrSrc)
{
HRESULT hr;
hr = AppendBSTR(bstrSrc.m_str);
if (FAILED(hr))
AtlThrow(hr);
return *this;
}
CComBSTR& operator+=(LPCOLESTR pszSrc)
{
HRESULT hr;
hr = Append(pszSrc);
if (FAILED(hr))
AtlThrow(hr);
return *this;
}

bool operator<(const CComBSTR& bstrSrc) const throw()
{
return VarBstrCmp(m_str, bstrSrc.m_str, LOCALE_USER_DEFAULT, 0) ==
VARCMP_LT;
}
bool operator<(LPCOLESTR pszSrc) const
{
CComBSTR bstr2(pszSrc);
return operator<(bstr2);
}
bool operator<(LPOLESTR pszSrc) const
{
return operator<((LPCOLESTR)pszSrc);
}

bool operator>(const CComBSTR& bstrSrc) const throw()
{
return VarBstrCmp(m_str, bstrSrc.m_str, LOCALE_USER_DEFAULT, 0) ==
VARCMP_GT;
}
bool operator>(LPCOLESTR pszSrc) const
{
CComBSTR bstr2(pszSrc);
return operator>(bstr2);
}
bool operator>(LPOLESTR pszSrc) const
{
return operator>((LPCOLESTR)pszSrc);
}

bool operator!=(const CComBSTR& bstrSrc) const throw()
{
return !operator==(bstrSrc);
}
bool operator!=(LPCOLESTR pszSrc) const
{
return !operator==(pszSrc);
}
bool operator!=(int nNull) const throw()
{
return !operator==(nNull);
}
bool operator!=(LPOLESTR pszSrc) const
{
return operator!=((LPCOLESTR)pszSrc);
}

bool operator==(const CComBSTR& bstrSrc) const throw()
{
return VarBstrCmp(m_str, bstrSrc.m_str, LOCALE_USER_DEFAULT, 0) ==
VARCMP_EQ;
}
bool operator==(LPCOLESTR pszSrc) const
{
CComBSTR bstr2(pszSrc);
return operator==(bstr2);
}
bool operator==(LPOLESTR pszSrc) const
{
return operator==((LPCOLESTR)pszSrc);
}

bool operator==(int nNull) const throw()
{
ATLASSERT(nNull == NULL);
(void)nNull;
return (m_str == NULL);
}
CComBSTR(LPCSTR pSrc)
{
if (pSrc != NULL)
{
m_str = A2WBSTR(pSrc);
if (m_str == NULL)
AtlThrow(E_OUTOFMEMORY);
}
else
m_str = NULL;
}

CComBSTR(int nSize, LPCSTR sz)
{
if (nSize != 0 && sz == NULL)
{
m_str = ::SysAllocStringLen(NULL, nSize);
if (m_str == NULL)
AtlThrow(E_OUTOFMEMORY);
return;
}

m_str = A2WBSTR(sz, nSize);
if (m_str == NULL && nSize != 0)
AtlThrow(E_OUTOFMEMORY);
}

HRESULT Append(LPCSTR lpsz) throw()
{
if (lpsz == NULL)
return S_OK;

CComBSTR bstrTemp;
ATLTRY(bstrTemp = lpsz);
if (bstrTemp.m_str == NULL)
return E_OUTOFMEMORY;
return Append(bstrTemp);
}

CComBSTR& operator=(LPCSTR pSrc)
{
::SysFreeString(m_str);
m_str = A2WBSTR(pSrc);
if (m_str == NULL && pSrc != NULL)
AtlThrow(E_OUTOFMEMORY);
return *this;
}
bool operator<(LPCSTR pszSrc) const
{
CComBSTR bstr2(pszSrc);
return operator<(bstr2);
}
bool operator>(LPCSTR pszSrc) const
{
CComBSTR bstr2(pszSrc);
return operator>(bstr2);
}
bool operator!=(LPCSTR pszSrc) const
{
return !operator==(pszSrc);
}
bool operator==(LPCSTR pszSrc) const
{
CComBSTR bstr2(pszSrc);
return operator==(bstr2);
}
HRESULT WriteToStream(IStream* pStream) throw()
{
ATLASSERT(pStream != NULL);
if(pStream == NULL)
return E_INVALIDARG;

ULONG cb;
ULONG cbStrLen = ULONG(m_str ? SysStringByteLen(m_str)+sizeof(OLECHAR) :
0);
HRESULT hr = pStream->Write((void*) &cbStrLen, sizeof(cbStrLen), &cb);
if (FAILED(hr))
return hr;
return cbStrLen ? pStream->Write((void*) m_str, cbStrLen, &cb) : S_OK;
}
HRESULT ReadFromStream(IStream* pStream) throw()
{
ATLASSERT(pStream != NULL);
if(pStream == NULL)
return E_INVALIDARG;

ATLASSERT(m_str == NULL); // should be empty
Empty();

ULONG cbStrLen = 0;
HRESULT hr = pStream->Read((void*) &cbStrLen, sizeof(cbStrLen), NULL);
if ((hr == S_OK) && (cbStrLen != 0))
{
//subtract size for terminating NULL which we wrote out
//since SysAllocStringByteLen overallocates for the NULL
m_str = SysAllocStringByteLen(NULL, cbStrLen-sizeof(OLECHAR));
if (m_str == NULL)
hr = E_OUTOFMEMORY;
else
hr = pStream->Read((void*) m_str, cbStrLen, NULL);
// If SysAllocStringByteLen or IStream::Read failed, reset seek
// pointer to start of BSTR size.
if (hr != S_OK)
{
LARGE_INTEGER nOffset;
nOffset.QuadPart = -(static_cast<LONGLONG>(sizeof(cbStrLen)));
pStream->Seek(nOffset, STREAM_SEEK_CUR, NULL);
}
}
if (hr == S_FALSE)
hr = E_FAIL;
return hr;
}
static bool LoadStringResource(HINSTANCE hInstance, UINT uID, BSTR&
bstrText) throw()
{
const ATLSTRINGRESOURCEIMAGE* pImage;

ATLASSERT(bstrText == NULL);

pImage = AtlGetStringResourceImage(hInstance, uID);
if (pImage != NULL)
{
bstrText = ::SysAllocStringLen(pImage->achString, pImage->nLength);
}

return (bstrText != NULL) ? true : false;
}

static bool LoadStringResource(UINT uID, BSTR& bstrText) throw()
{
const ATLSTRINGRESOURCEIMAGE* pImage;

ATLASSERT(bstrText == NULL);

pImage = AtlGetStringResourceImage(uID);
if (pImage != NULL)
{
bstrText = ::SysAllocStringLen(pImage->achString, pImage->nLength);
}

return (bstrText != NULL) ? true : false;
}

// each character in BSTR is copied to each element in SAFEARRAY
HRESULT BSTRToArray(LPSAFEARRAY *ppArray) throw()
{
return VectorFromBstr(m_str, ppArray);
}

// first character of each element in SAFEARRAY is copied to BSTR
HRESULT ArrayToBSTR(const SAFEARRAY *pSrc) throw()
{
::SysFreeString(m_str);
return BstrFromVector((LPSAFEARRAY)pSrc, &m_str);
}
};

Nov 17 '05 #2

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

1
by: Matt | last post by:
In test() method: var path="C:\test\hello.txt"; //returns -1 for path.lastIndexOf("\\"). why?? var pos=path.lastIndexOf("\\"); //return -1 But in showFile() method: We are able to get the...
2
by: Ivar | last post by:
Hi, string s = "aXXa"; Console.WriteLine(s.LastIndexOf("XX",0)); Console.WriteLine(s.LastIndexOf("XX")); Console.WriteLine(s.IndexOf("XX",0)); Console.WriteLine(s.IndexOf("XX")); Result:...
9
by: sklett | last post by:
string url = http://localhost/subPath/Default.aspx; k = url.LastIndexOf("/", 0, url.Length); This throws an exception: Count must be positive and count must refer to a location within the...
3
by: Terry Olsen | last post by:
I have a string that is approximately 600 characters. I need to divide up the string into 400 character chunks. I'm having trouble with using LastIndexOf. The following code returns a value of...
0
by: ccshine via DotNetMonster.com | last post by:
I'm working on an app that implements a Structure to store a recordset in an ArrayList. I used this setup to bind to a DataGrid and it worked out so well, I thought it might be a better solution...
0
by: ccshine | last post by:
I'm working on an app that implements a Structure to store a recordset in an ArrayList. I used this setup to bind to a DataGrid and it worked out so well, I thought it might be a better solution...
3
by: =?Utf-8?B?dmluYXk=?= | last post by:
I am trying to check LastIndexOf "\" in a File Path, if it not there i want to append a "\" for some reason it throws error? int index = filePath.LastIndexOf("\"); Any suggestions... --
1
chunk1978
by: chunk1978 | last post by:
hello... i'm trying to copy the file name of a string into a text box after a user selects a file from their computer. that part works fine. however, Macs and PCs write file paths differently: ...
9
by: senfo | last post by:
I realize it's Friday and I'm probably already on vacation for the remainder of the day; but, I have a really, really stupid question. Is there a bug in the .NET 2.0 Framework in regards to the...
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.