Skip to content

Commit

Permalink
KVS implementation for darwin (#5668)
Browse files Browse the repository at this point in the history
* Added a darwin KVS implementation

* Restyle fixes

* Fix darwin build, fix relative path handling in darwin KVS

* Add coredata dependency in CHIP project, remove obsolete logging style gn argument

* Restyle fixes

* Remove commented out code
  • Loading branch information
andy31415 authored Apr 7, 2021
1 parent 5d00d1c commit 086bdbb
Show file tree
Hide file tree
Showing 6 changed files with 265 additions and 50 deletions.
2 changes: 2 additions & 0 deletions src/darwin/Framework/CHIP.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,8 @@
LIBRARY_SEARCH_PATHS = "$(TEMP_DIR)/out/lib";
OTHER_LDFLAGS = "";
"OTHER_LDFLAGS[sdk=*]" = (
"-framework",
CoreData,
"-framework",
Foundation,
"-framework",
Expand Down
1 change: 0 additions & 1 deletion src/darwin/Framework/chip_xcode_build_connector.sh
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ done
declare -a args=(
'default_configs_cosmetic=[]' # suppress colorization
'chip_crypto="mbedtls"'
'chip_logging_style="darwin"'
'chip_build_tools=false'
'chip_build_tests=false'
'chip_ble_project_config_include=""'
Expand Down
8 changes: 3 additions & 5 deletions src/platform/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ if (chip_device_platform != "none" && chip_device_platform != "external") {
config("platform_config") {
if (chip_device_platform == "darwin") {
frameworks = [
"CoreData.framework",
"CoreFoundation.framework",
"CoreBluetooth.framework",
"Foundation.framework",
Expand Down Expand Up @@ -312,6 +313,8 @@ if (chip_device_platform != "none" && chip_device_platform != "external") {
"Darwin/ConnectivityManagerImpl.cpp",
"Darwin/ConnectivityManagerImpl.h",
"Darwin/InetPlatformConfig.h",
"Darwin/KeyValueStoreManagerImpl.h",
"Darwin/KeyValueStoreManagerImpl.mm",
"Darwin/Logging.cpp",
"Darwin/MdnsError.cpp",
"Darwin/MdnsError.h",
Expand All @@ -324,11 +327,6 @@ if (chip_device_platform != "none" && chip_device_platform != "external") {
"Darwin/SystemPlatformConfig.h",
]

sources += [
"Darwin/KeyValueStoreManagerImpl.cpp",
"Darwin/KeyValueStoreManagerImpl.h",
]

if (chip_enable_ble) {
sources += [
"Darwin/BleApplicationDelegate.h",
Expand Down
34 changes: 0 additions & 34 deletions src/platform/Darwin/KeyValueStoreManagerImpl.cpp

This file was deleted.

14 changes: 4 additions & 10 deletions src/platform/Darwin/KeyValueStoreManagerImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,10 @@ class KeyValueStoreManagerImpl final : public KeyValueStoreManager
friend class KeyValueStoreManager;

public:
// NOTE: Currently this platform does not support partial and offset reads
// these will return CHIP_ERROR_NOT_IMPLEMENTED.
CHIP_ERROR _Get(const char * key, void * value, size_t value_size, size_t * read_bytes_size = nullptr, size_t offset = 0)
{
return CHIP_ERROR_NOT_IMPLEMENTED;
}

CHIP_ERROR _Delete(const char * key) { return CHIP_ERROR_NOT_IMPLEMENTED; }

CHIP_ERROR _Put(const char * key, const void * value, size_t value_size) { return CHIP_ERROR_NOT_IMPLEMENTED; }
CHIP_ERROR Init(const char * fileName);
CHIP_ERROR _Get(const char * key, void * value, size_t value_size, size_t * read_bytes_size = nullptr, size_t offset = 0);
CHIP_ERROR _Delete(const char * key);
CHIP_ERROR _Put(const char * key, const void * value, size_t value_size);

private:
// ===== Members for internal use by the following friends.
Expand Down
256 changes: 256 additions & 0 deletions src/platform/Darwin/KeyValueStoreManagerImpl.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
/*
*
* Copyright (c) 2021 Project CHIP Authors
* All rights reserved.
*
* 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.
*/

/**
* @file
* Platform-specific key value storage implementation for Darwin
*/

#include <platform/KeyValueStoreManager.h>

#include <algorithm>

#include <support/CodeUtils.h>

#import <CoreData/CoreData.h>
#import <CoreFoundation/CoreFoundation.h>

@interface KeyValueItem : NSManagedObject

@property (nonatomic, retain) NSString * key;
@property (nonatomic, retain) NSData * value;

@end

@implementation KeyValueItem

@synthesize key;
@synthesize value;

- (instancetype)initWithContext:(nonnull NSManagedObjectContext *)context
key:(nonnull NSString *)key_
value:(nonnull NSData *)value_
{
if (self = [super initWithContext:context]) {
key = key_;
value = value_;
}
return self;
}

@end

namespace chip {
namespace DeviceLayer {
namespace PersistedStorage {
namespace {

NSManagedObjectContext * gContext = nullptr;

NSManagedObjectModel * CreateManagedObjectModel()
{
NSManagedObjectModel * model = [[NSManagedObjectModel alloc] init];

// create the entity
NSEntityDescription * entity = [[NSEntityDescription alloc] init];
[entity setName:@"KeyValue"];
[entity setManagedObjectClassName:@"KeyValueItem"];

// create the attributes
NSMutableArray * properties = [NSMutableArray array];

NSAttributeDescription * keyAttribute = [[NSAttributeDescription alloc] init];
[keyAttribute setName:@"key"];
[keyAttribute setAttributeType:NSStringAttributeType];
[keyAttribute setOptional:NO];
[properties addObject:keyAttribute];

NSAttributeDescription * valueAttribute = [[NSAttributeDescription alloc] init];
[valueAttribute setName:@"value"];
[valueAttribute setAttributeType:NSBinaryDataAttributeType];
[valueAttribute setOptional:NO];
[properties addObject:valueAttribute];

NSFetchIndexElementDescription * elementIndex =
[[NSFetchIndexElementDescription alloc] initWithProperty:keyAttribute
collationType:NSFetchIndexElementTypeBinary];
elementIndex.ascending = true;

NSFetchIndexDescription * keyIndexDescription =
[[NSFetchIndexDescription alloc] initWithName:@"kv_item_key"
elements:[[NSArray alloc] initWithObjects:elementIndex, nil]];

// add attributes to entity
[entity setProperties:properties];
[entity setIndexes:[[NSArray alloc] initWithObjects:keyIndexDescription, nil]];

// add entity to model
[model setEntities:[NSArray arrayWithObject:entity]];

return model;
}

KeyValueItem * FindItemForKey(NSString * key, NSError ** error)
{
NSFetchRequest * request = [[NSFetchRequest alloc] initWithEntityName:@"KeyValue"];
request.predicate = [NSPredicate predicateWithFormat:@"key = %@", key];

NSArray * result = [gContext executeFetchRequest:request error:error];
if (result == nil) {
return nullptr;
}

if (result.count == 0) {
return nullptr;
}
return (KeyValueItem *) [result objectAtIndex:0];
}

}

KeyValueStoreManagerImpl KeyValueStoreManagerImpl::sInstance;

CHIP_ERROR KeyValueStoreManagerImpl::Init(const char * fileName)
{
ReturnErrorCodeIf(gContext != nullptr, CHIP_ERROR_INCORRECT_STATE);
ReturnErrorCodeIf(fileName == nullptr, CHIP_ERROR_INVALID_ARGUMENT);
ReturnErrorCodeIf(fileName[0] == '\0', CHIP_ERROR_INVALID_ARGUMENT);

NSURL * url = nullptr;

// relative paths are relative to Documents folder
if (fileName[0] != '/') {
NSURL * documentsDirectory = [NSFileManager.defaultManager URLForDirectory:NSDocumentDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
create:YES
error:nil];
if (documentsDirectory == nullptr) {
ChipLogError(DeviceLayer, "Failed to get documents directory.");
return CHIP_ERROR_INTERNAL;
}
ChipLogProgress(
DeviceLayer, "Found user documents directory: %s", [[documentsDirectory absoluteString] UTF8String]);

url = [NSURL URLWithString:[NSString stringWithUTF8String:fileName] relativeToURL:documentsDirectory];
} else {
url = [NSURL URLWithString:[NSString stringWithUTF8String:fileName]];
}
ReturnErrorCodeIf(url == nullptr, CHIP_ERROR_NO_MEMORY);

ChipLogProgress(DeviceLayer, "KVS will be written to: %s", [[url absoluteString] UTF8String]);

NSManagedObjectModel * model = CreateManagedObjectModel();
ReturnErrorCodeIf(model == nullptr, CHIP_ERROR_NO_MEMORY);

// setup persistent store coordinator

NSPersistentStoreCoordinator * coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];

NSError * error = nil;
if (![coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:nil error:&error]) {
ChipLogError(DeviceLayer, "Invalid store. Attempting to clear: %s", error.localizedDescription.UTF8String);
if (![[NSFileManager defaultManager] removeItemAtURL:url error:&error]) {
ChipLogError(DeviceLayer, "Failed to delete item: %s", error.localizedDescription.UTF8String);
}

if (![coordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:url
options:nil
error:&error]) {
ChipLogError(DeviceLayer, "Failed to initialize clear KVS storage: %s", error.localizedDescription.UTF8String);
chipDie();
}
}

// create Managed Object context
gContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[gContext setPersistentStoreCoordinator:coordinator];

return CHIP_NO_ERROR;
}

CHIP_ERROR KeyValueStoreManagerImpl::_Get(
const char * key, void * value, size_t value_size, size_t * read_bytes_size, size_t offset)
{
ReturnErrorCodeIf(key == nullptr, CHIP_ERROR_INVALID_ARGUMENT);
ReturnErrorCodeIf(offset != 0, CHIP_ERROR_INVALID_ARGUMENT);

KeyValueItem * item = FindItemForKey([[NSString alloc] initWithUTF8String:key], nil);
if (!item) {
return CHIP_ERROR_KEY_NOT_FOUND;
}

if (read_bytes_size != nullptr) {
*read_bytes_size = item.value.length;
}

if (value != nullptr) {
memcpy(value, item.value.bytes, std::min(item.value.length, value_size));
}

return CHIP_NO_ERROR;
}

CHIP_ERROR KeyValueStoreManagerImpl::_Delete(const char * key)
{
ReturnErrorCodeIf(key == nullptr, CHIP_ERROR_INVALID_ARGUMENT);

KeyValueItem * item = FindItemForKey([[NSString alloc] initWithUTF8String:key], nil);
if (!item) {
return CHIP_NO_ERROR;
}

[gContext deleteObject:item];

NSError * error = nil;
if (![gContext save:&error]) {
ChipLogError(DeviceLayer, "Error saving context: %s", error.localizedDescription.UTF8String);
return CHIP_ERROR_INTERNAL;
}

return CHIP_NO_ERROR;
}

CHIP_ERROR KeyValueStoreManagerImpl::_Put(const char * key, const void * value, size_t value_size)
{
ReturnErrorCodeIf(key == nullptr, CHIP_ERROR_INVALID_ARGUMENT);

NSData * data = [[NSData alloc] initWithBytes:value length:value_size];

KeyValueItem * item = FindItemForKey([[NSString alloc] initWithUTF8String:key], nil);
if (!item) {
item = [[KeyValueItem alloc] initWithContext:gContext key:[[NSString alloc] initWithUTF8String:key] value:data];
[gContext insertObject:item];
} else {
item.value = data;
}

NSError * error = nil;
if (![gContext save:&error]) {
ChipLogError(DeviceLayer, "Error saving context: %s", error.localizedDescription.UTF8String);
return CHIP_ERROR_INTERNAL;
}

return CHIP_NO_ERROR;
}

} // namespace PersistedStorage
} // namespace DeviceLayer
} // namespace chip

0 comments on commit 086bdbb

Please sign in to comment.