text:
<?php

namespace CC\PlaysureBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use CC\PlaysureBundle\Entity\Game;
use CC\PlaysureBundle\Entity\GameRepository;
use CC\PlaysureBundle\Entity\ExpertBet;
use CC\PlaysureBundle\Entity\ExpertBetRepository;
use CC\PlaysureBundle\Entity\Bet;
use CC\PlaysureBundle\Entity\BetRepository;

class BetController extends Controller
{
    public function placeNewBetAction($gameId, $expertBetId)
    {
        $bet = new Bet();

        $user = $this->container->get('security.context')->getToken()->getUser();
        $userId = $user->getId();

        $bet->setGameId($gameId);
        $bet->setexpertBetId($expertBetId);
        $bet->setuserId($userId);

        $em = $this->getDoctrine()->getManager();

        $em->persist($bet);
        $em->flush();

        $betId = $bet->getId();

        $variablesToRender = array(
                'userId' =>  $userId,
                );

        return $this->redirect($this->generateUrl('userBets', $variablesToRender));

    }
    public function displayUserBetsAction($userId)
    {
        $betRepository = $this->getDoctrine()->getRepository('CCPlaysureBundle:Bet');
        $gameRepository = $this->getDoctrine()->getRepository('CCPlaysureBundle:Game');
        $expertBetRepository = $this->getDoctrine()->getRepository('CCPlaysureBundle:ExpertBet');
        $expertRepository = $this->getDoctrine()->getRepository('CCPlaysureBundle:Expert');
        // get expert

        $bets = $betRepository->getBetsByUserId($userId);

        foreach ($bets as $bet) {

            // get game
            $betId = $bet->getId();
            $game = $gameRepository->getGame($bet->getGameId());

           $teamA = $game->getTeamA();
           $teamB = $game->getTeamB();

           $bet->teamA = $teamA;
           $bet->teamB = $teamB;

           $expertBetId = $bet->getExpertBetId();
           $expertBet = $expertBetRepository->getExpertBet($expertBetId);

            $pronosticTeamA = $expertBet->getPronosticTeamA();
            $pronosticTeamB = $expertBet->getPronosticTeamB();

            $bet->pronosticTeamA = $pronosticTeamA;
            $bet->pronosticTeamB = $pronosticTeamB;

            $expertId = $expertBet->getExpertId();
            $expert = $expertRepository->getExpert($expertId);

            $expertName = $expert->getName();
            $bet->expertName = $expertName;

            $bet->expertBet = $expertBet;

        }

        $variablesToRender = array(
                'bets' =>  $bets,
                );

        return $this->render('CCPlaysureBundle:Home:userBets.html.twig', $variablesToRender);
    }

}


text:
/***********************************************************************
 * Module:  ActivityInventory.cs
 * Author:  Rudy
 * Purpose: Definition of the Class ActivityInventory
 ***********************************************************************/

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Graphics.Display;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

namespace PomodoroKeeper.Model
{
	public class ActivityInventory
	{
		private const int MaxCount = 100;

		private int TaskCount { get; set; }

		private string Title { get; set; }

		//===============InventoryTaskList template==========
		#region
		public List<InventoryTask> InventoryTaskList;

		/// <pdGenerated>default getter</pdGenerated>
		public List<InventoryTask> GetInventroyTask()
		{
			if (InventoryTaskList == null)
				InventoryTaskList = new List<InventoryTask>();
			return InventoryTaskList;
		}

		/// <pdGenerated>default setter</pdGenerated>
		public void SetInventroyTask(List<InventoryTask> newInventoryTaskList)
		{
			RemoveAllInventroyTask();
			foreach (InventoryTask oInventoryTask in newInventoryTaskList)
				AddInventroyTask(oInventoryTask);
		}

		/// <pdGenerated>default Add</pdGenerated>
		public void AddInventroyTask(InventoryTask newInventoryTask)
		{
			if (newInventoryTask == null)
				return;
			if (this.InventoryTaskList == null)
				this.InventoryTaskList = new List<InventoryTask>();
			if (!this.InventoryTaskList.Contains(newInventoryTask))
				this.InventoryTaskList.Add(newInventoryTask);
		}

		/// <pdGenerated>default Remove</pdGenerated>
		public void RemoveInventroyTask(InventoryTask oldInventoryTask)
		{
			if (oldInventoryTask == null)
				return;
			if (this.InventoryTaskList != null)
				if (this.InventoryTaskList.Contains(oldInventoryTask))
					this.InventoryTaskList.Remove(oldInventoryTask);
		}

		/// <pdGenerated>default removeAll</pdGenerated>
		public void RemoveAllInventroyTask()
		{
			if (InventoryTaskList != null)
				InventoryTaskList.Clear();
		}
		#endregion
	}
}

redpajama_set_name: RedPajamaGithub


text:
package org.hl7.fhir.dstu3.model.valuesets;

import org.hl7.fhir.dstu3.model.EnumFactory;

public class V3DocumentCompletionEnumFactory implements EnumFactory<V3DocumentCompletion> {

  public V3DocumentCompletion fromCode(String codeString) throws IllegalArgumentException {
    if (codeString == null || "".equals(codeString))
      return null;
    if ("AU".equals(codeString))
      return V3DocumentCompletion.AU;
    if ("DI".equals(codeString))
      return V3DocumentCompletion.DI;
    if ("DO".equals(codeString))
      return V3DocumentCompletion.DO;
    if ("IN".equals(codeString))
      return V3DocumentCompletion.IN;
    if ("IP".equals(codeString))
      return V3DocumentCompletion.IP;
    if ("LA".equals(codeString))
      return V3DocumentCompletion.LA;
    if ("NU".equals(codeString))
      return V3DocumentCompletion.NU;
    if ("PA".equals(codeString))
      return V3DocumentCompletion.PA;
    if ("UC".equals(codeString))
      return V3DocumentCompletion.UC;
    throw new IllegalArgumentException("Unknown V3DocumentCompletion code '"+codeString+"'");
  }

  public String toCode(V3DocumentCompletion code) {
    if (code == V3DocumentCompletion.AU)
      return "AU";
    if (code == V3DocumentCompletion.DI)
      return "DI";
    if (code == V3DocumentCompletion.DO)
      return "DO";
    if (code == V3DocumentCompletion.IN)
      return "IN";
    if (code == V3DocumentCompletion.IP)
      return "IP";
    if (code == V3DocumentCompletion.LA)
      return "LA";
    if (code == V3DocumentCompletion.NU)
      return "NU";
    if (code == V3DocumentCompletion.PA)
      return "PA";
    if (code == V3DocumentCompletion.UC)
      return "UC";
    return "?";
  }

    public String toSystem(V3DocumentCompletion code) {
      return code.getSystem();
      }

}


text:
quickServerTest
===============

For quick server testing


text:
/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/

"use strict";

// Simulations show these probabilities for a single change
// 93.1% that one group is invalidated
// 4.8% that two groups are invalidated
// 1.1% that 3 groups are invalidated
// 0.1% that 4 or more groups are invalidated
//
// And these for removing/adding 10 lexically adjacent files
// 64.5% that one group is invalidated
// 24.8% that two groups are invalidated
// 7.8% that 3 groups are invalidated
// 2.7% that 4 or more groups are invalidated
//
// And these for removing/adding 3 random files
// 0% that one group is invalidated
// 3.7% that two groups are invalidated
// 80.8% that 3 groups are invalidated
// 12.3% that 4 groups are invalidated
// 3.2% that 5 or more groups are invalidated

/**
 *
 * @param {string} a key
 * @param {string} b key
 * @returns {number} the similarity as number
 */
const similarity = (a, b) => {
	const l = Math.min(a.length, b.length);
	let dist = 0;
	for (let i = 0; i < l; i++) {
		const ca = a.charCodeAt(i);
		const cb = b.charCodeAt(i);
		dist += Math.max(0, 10 - Math.abs(ca - cb));
	}
	return dist;
};

/**
 * @param {string} a key
 * @param {string} b key
 * @param {Set<string>} usedNames set of already used names
 * @returns {string} the common part and a single char for the difference
 */
const getName = (a, b, usedNames) => {
	const l = Math.min(a.length, b.length);
	let i = 0;
	while (i < l) {
		if (a.charCodeAt(i) !== b.charCodeAt(i)) {
			i++;
			break;
		}
		i++;
	}
	while (i < l) {
		const name = a.slice(0, i);
		const lowerName = name.toLowerCase();
		if (!usedNames.has(lowerName)) {
			usedNames.add(lowerName);
			return name;
		}
		i++;
	}
	// names always contain a hash, so this is always unique
	// we don't need to check usedNames nor add it
	return a;
};

/**
 * @param {Record<string, number>} total total size
 * @param {Record<string, number>} size single size
 * @returns {void}
 */
const addSizeTo = (total, size) => {
	for (const key of Object.keys(size)) {
		total[key] = (total[key] || 0) + size[key];
	}
};

/**
 * @param {Record<string, number>} total total size
 * @param {Record<string, number>} size single size
 * @returns {void}
 */
const subtractSizeFrom = (total, size) => {
	for (const key of Object.keys(size)) {
		total[key] -= size[key];
	}
};

/**
 * @param {Iterable<Node>} nodes some nodes
 * @returns {Record<string, number>} total size
 */
const sumSize = nodes => {
	const sum = Object.create(null);
	for (const node of nodes) {
		addSizeTo(sum, node.size);
	}
	return sum;
};

const isTooBig = (size, maxSize) => {
	for (const key of Object.keys(size)) {
		const s = size[key];
		if (s === 0) continue;
		const maxSizeValue = maxSize[key];
		if (typeof maxSizeValue === "number") {
			if (s > maxSizeValue) return true;
		}
	}
	return false;
};

const isTooSmall = (size, minSize) => {
	for (const key of Object.keys(size)) {
		const s = size[key];
		if (s === 0) continue;
		const minSizeValue = minSize[key];
		if (typeof minSizeValue === "number") {
			if (s < minSizeValue) return true;
		}
	}
	return false;
};

const getTooSmallTypes = (size, minSize) => {
	const types = new Set();
	for (const key of Object.keys(size)) {
		const s = size[key];
		if (s === 0) continue;
		const minSizeValue = minSize[key];
		if (typeof minSizeValue === "number") {
			if (s < minSizeValue) types.add(key);
		}
	}
	return types;
};

const getNumberOfMatchingSizeTypes = (size, types) => {
	let i = 0;
	for (const key of Object.keys(size)) {
		if (size[key] !== 0 && types.has(key)) i++;
	}
	return i;
};

const selectiveSizeSum = (size, types) => {
	let sum = 0;
	for (const key of Object.keys(size)) {
		if (size[key] !== 0 && types.has(key)) sum += size[key];
	}
	return sum;
};

/**
 * @template T
 */
class Node {
	/**
	 * @param {T} item item
	 * @param {string} key key
	 * @param {Record<string, number>} size size
	 */
	constructor(item, key, size) {
		this.item = item;
		this.key = key;
		this.size = size;
	}
}

/**
 * @template T
 */
class Group {
	/**
	 * @param {Node<T>[]} nodes nodes
	 * @param {number[]} similarities similarities between the nodes (length = nodes.length - 1)
	 * @param {Record<string, number>=} size size of the group
	 */
	constructor(nodes, similarities, size) {
		this.nodes = nodes;
		this.similarities = similarities;
		this.size = size || sumSize(nodes);
		/** @type {string} */
		this.key = undefined;
	}

	/**
	 * @param {function(Node): boolean} filter filter function
	 * @returns {Node[]} removed nodes
	 */
	popNodes(filter) {
		const newNodes = [];
		const newSimilarities = [];
		const resultNodes = [];
		let lastNode;
		for (let i = 0; i < this.nodes.length; i++) {
			const node = this.nodes[i];
			if (filter(node)) {
				resultNodes.push(node);
			} else {
				if (newNodes.length > 0) {
					newSimilarities.push(
						lastNode === this.nodes[i - 1]
							? this.similarities[i - 1]
							: similarity(lastNode.key, node.key)
					);
				}
				newNodes.push(node);
				lastNode = node;
			}
		}
		if (resultNodes.length === this.nodes.length) return undefined;
		this.nodes = newNodes;
		this.similarities = newSimilarities;
		this.size = sumSize(newNodes);
		return resultNodes;
	}
}

/**
 * @param {Iterable<Node>} nodes nodes
 * @returns {number[]} similarities
 */
const getSimilarities = nodes => {
	// calculate similarities between lexically adjacent nodes
	/** @type {number[]} */
	const similarities = [];
	let last = undefined;
	for (const node of nodes) {
		if (last !== undefined) {
			similarities.push(similarity(last.key, node.key));
		}
		last = node;
	}
	return similarities;
};

/**
 * @template T
 * @typedef {Object} GroupedItems<T>
 * @property {string} key
 * @property {T[]} items
 * @property {Record<string, number>} size
 */

/**
 * @template T
 * @typedef {Object} Options
 * @property {Record<string, number>} maxSize maximum size of a group
 * @property {Record<string, number>} minSize minimum size of a group (preferred over maximum size)
 * @property {Iterable<T>} items a list of items
 * @property {function(T): Record<string, number>} getSize function to get size of an item
 * @property {function(T): string} getKey function to get the key of an item
 */

/**
 * @template T
 * @param {Options<T>} options options object
 * @returns {GroupedItems<T>[]} grouped items
 */
module.exports = ({ maxSize, minSize, items, getSize, getKey }) => {
	/** @type {Group<T>[]} */
	const result = [];

	const nodes = Array.from(
		items,
		item => new Node(item, getKey(item), getSize(item))
	);

	/** @type {Node<T>[]} */
	const initialNodes = [];

	// lexically ordering of keys
	nodes.sort((a, b) => {
		if (a.key < b.key) return -1;
		if (a.key > b.key) return 1;
		return 0;
	});

	// return nodes bigger than maxSize directly as group
	// But make sure that minSize is not violated
	for (const node of nodes) {
		if (isTooBig(node.size, maxSize) && !isTooSmall(node.size, minSize)) {
			result.push(new Group([node], []));
		} else {
			initialNodes.push(node);
		}
	}

	if (initialNodes.length > 0) {
		const initialGroup = new Group(initialNodes, getSimilarities(initialNodes));

		const removeProblematicNodes = (group, consideredSize = group.size) => {
			const problemTypes = getTooSmallTypes(consideredSize, minSize);
			if (problemTypes.size > 0) {
				// We hit an edge case where the working set is already smaller than minSize
				// We merge problematic nodes with the smallest result node to keep minSize intact
				const problemNodes = group.popNodes(
					n => getNumberOfMatchingSizeTypes(n.size, problemTypes) > 0
				);
				if (problemNodes === undefined) return false;
				// Only merge it with result nodes that have the problematic size type
				const possibleResultGroups = result.filter(
					n => getNumberOfMatchingSizeTypes(n.size, problemTypes) > 0
				);
				if (possibleResultGroups.length > 0) {
					const bestGroup = possibleResultGroups.reduce((min, group) => {
						const minMatches = getNumberOfMatchingSizeTypes(min, problemTypes);
						const groupMatches = getNumberOfMatchingSizeTypes(
							group,
							problemTypes
						);
						if (minMatches !== groupMatches)
							return minMatches < groupMatches ? group : min;
						if (
							selectiveSizeSum(min.size, problemTypes) >
							selectiveSizeSum(group.size, problemTypes)
						)
							return group;
						return min;
					});
					for (const node of problemNodes) bestGroup.nodes.push(node);
					bestGroup.nodes.sort((a, b) => {
						if (a.key < b.key) return -1;
						if (a.key > b.key) return 1;
						return 0;
					});
				} else {
					// There are no other nodes with the same size types
					// We create a new group and have to accept that it's smaller than minSize
					result.push(new Group(problemNodes, null));
				}
				return true;
			} else {
				return false;
			}
		};

		if (initialGroup.nodes.length > 0) {
			const queue = [initialGroup];

			while (queue.length) {
				const group = queue.pop();
				// only groups bigger than maxSize need to be splitted
				if (!isTooBig(group.size, maxSize)) {
					result.push(group);
					continue;
				}
				// If the group is already too small
				// we try to work only with the unproblematic nodes
				if (removeProblematicNodes(group)) {
					// This changed something, so we try this group again
					queue.push(group);
					continue;
				}

				// find unsplittable area from left and right
				// going minSize from left and right
				// at least one node need to be included otherwise we get stuck
				let left = 1;
				let leftSize = Object.create(null);
				addSizeTo(leftSize, group.nodes[0].size);
				while (left < group.nodes.length && isTooSmall(leftSize, minSize)) {
					addSizeTo(leftSize, group.nodes[left].size);
					left++;
				}
				let right = group.nodes.length - 2;
				let rightSize = Object.create(null);
				addSizeTo(rightSize, group.nodes[group.nodes.length - 1].size);
				while (right >= 0 && isTooSmall(rightSize, minSize)) {
					addSizeTo(rightSize, group.nodes[right].size);
					right--;
				}

				//      left v   v right
				// [ O O O ] O O O [ O O O ]
				// ^^^^^^^^^ leftSize
				//       rightSize ^^^^^^^^^
				// leftSize > minSize
				// rightSize > minSize

				// Perfect split: [ O O O ] [ O O O ]
				//                right === left - 1

				if (left - 1 > right) {
					// We try to remove some problematic nodes to "fix" that
					let prevSize;
					if (right < group.nodes.length - left) {
						subtractSizeFrom(rightSize, group.nodes[right + 1].size);
						prevSize = rightSize;
					} else {
						subtractSizeFrom(leftSize, group.nodes[left - 1].size);
						prevSize = leftSize;
					}
					if (removeProblematicNodes(group, prevSize)) {
						// This changed something, so we try this group again
						queue.push(group);
						continue;
					}
					// can't split group while holding minSize
					// because minSize is preferred of maxSize we return
					// the problematic nodes as result here even while it's too big
					// To avoid this make sure maxSize > minSize * 3
					result.push(group);
					continue;
				}
				if (left <= right) {
					// when there is a area between left and right
					// we look for best split point
					// we split at the minimum similarity
					// here key space is separated the most
					// But we also need to make sure to not create too small groups
					let best = -1;
					let bestSimilarity = Infinity;
					let pos = left;
					let rightSize = sumSize(group.nodes.slice(pos));

					//       pos v   v right
					// [ O O O ] O O O [ O O O ]
					// ^^^^^^^^^ leftSize
					// rightSize ^^^^^^^^^^^^^^^

					while (pos <= right + 1) {
						const similarity = group.similarities[pos - 1];
						if (
							similarity < bestSimilarity &&
							!isTooSmall(leftSize, minSize) &&
							!isTooSmall(rightSize, minSize)
						) {
							best = pos;
							bestSimilarity = similarity;
						}
						addSizeTo(leftSize, group.nodes[pos].size);
						subtractSizeFrom(rightSize, group.nodes[pos].size);
						pos++;
					}
					if (best < 0) {
						// This can't happen
						// but if that assumption is wrong
						// fallback to a big group
						result.push(group);
						continue;
					}
					left = best;
					right = best - 1;
				}

				// create two new groups for left and right area
				// and queue them up
				const rightNodes = [group.nodes[right + 1]];
				/** @type {number[]} */
				const rightSimilarities = [];
				for (let i = right + 2; i < group.nodes.length; i++) {
					rightSimilarities.push(group.similarities[i - 1]);
					rightNodes.push(group.nodes[i]);
				}
				queue.push(new Group(rightNodes, rightSimilarities));

				const leftNodes = [group.nodes[0]];
				/** @type {number[]} */
				const leftSimilarities = [];
				for (let i = 1; i < left; i++) {
					leftSimilarities.push(group.similarities[i - 1]);
					leftNodes.push(group.nodes[i]);
				}
				queue.push(new Group(leftNodes, leftSimilarities));
			}
		}
	}

	// lexically ordering
	result.sort((a, b) => {
		if (a.nodes[0].key < b.nodes[0].key) return -1;
		if (a.nodes[0].key > b.nodes[0].key) return 1;
		return 0;
	});

	// give every group a name
	const usedNames = new Set();
	for (let i = 0; i < result.length; i++) {
		const group = result[i];
		if (group.nodes.length === 1) {
			group.key = group.nodes[0].key;
		} else {
			const first = group.nodes[0];
			const last = group.nodes[group.nodes.length - 1];
			const name = getName(first.key, last.key, usedNames);
			group.key = name;
		}
	}

	// return the results
	return result.map(group => {
		/** @type {GroupedItems<T>} */
		return {
			key: group.key,
			items: group.nodes.map(node => node.item),
			size: group.size
		};
	});
};


text:
from os.path import join
from collections import namedtuple

# Base Path for ceph
base_path = '/var/lib/ceph'

# Base run Path
base_run_path = '/var/run/ceph'

tmp_path = join(base_path, 'tmp')

mon_path = join(base_path, 'mon')

mds_path = join(base_path, 'mds')

osd_path = join(base_path, 'osd')

# Default package components to install
_base_components = [
    'ceph-osd',
    'ceph-mds',
    'ceph-mon',
]

default_components = namedtuple('DefaultComponents', ['rpm', 'deb'])

# the difference here is because RPMs currently name the radosgw differently than DEBs.
# TODO: This needs to get unified once the packaging naming gets consistent
default_components.rpm = tuple(_base_components + ['ceph-radosgw'])
default_components.deb = tuple(_base_components + ['radosgw'])


text:
#include "../../SDL_internal.h"

#if SDL_VIDEO_DRIVER_ANDROID

#include "SDL_syswm.h"
#include "../SDL_sysvideo.h"
#include "../../events/SDL_keyboard_c.h"
#include "../../events/SDL_mouse_c.h"
#include "../../events/SDL_windowevents_c.h"
#include "../../core/android/SDL_android.h"

#include "SDL_androidvideo.h"
#include "SDL_androidwindow.h"
#include "SDL_hints.h"

/* Currently only one window */
SDL_Window *Android_Window = NULL;

int
Android_CreateWindow(_THIS, SDL_Window * window)
{
    SDL_WindowData *data;
    int retval = 0;

    Android_ActivityMutex_Lock_Running();

    if (Android_Window) {
        retval = SDL_SetError("Android only supports one window");
        goto endfunction;
    }

    /* Set orientation */
    Android_JNI_SetOrientation(window->w, window->h, window->flags & SDL_WINDOW_RESIZABLE, SDL_GetHint(SDL_HINT_ORIENTATIONS));

    /* Adjust the window data to match the screen */
    window->x = 0;
    window->y = 0;
    window->w = Android_SurfaceWidth;
    window->h = Android_SurfaceHeight;

    window->flags &= ~SDL_WINDOW_HIDDEN;
    window->flags |= SDL_WINDOW_SHOWN;          /* only one window on Android */

    /* One window, it always has focus */
    SDL_SetMouseFocus(window);
    SDL_SetKeyboardFocus(window);

    data = (SDL_WindowData *) SDL_calloc(1, sizeof(*data));
    if (!data) {
        retval = SDL_OutOfMemory();
        goto endfunction;
    }

    data->native_window = Android_JNI_GetNativeWindow();

    if (!data->native_window) {
        SDL_free(data);
        retval = SDL_SetError("Could not fetch native window");
        goto endfunction;
    }

    /* Do not create EGLSurface for Vulkan window since it will then make the window
       incompatible with vkCreateAndroidSurfaceKHR */
    if ((window->flags & SDL_WINDOW_OPENGL) != 0) {
        data->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType) data->native_window);

        if (data->egl_surface == EGL_NO_SURFACE) {
            ANativeWindow_release(data->native_window);
            SDL_free(data);
            retval = -1;
            goto endfunction;
        }
    }

    window->driverdata = data;
    Android_Window = window;

endfunction:

    SDL_UnlockMutex(Android_ActivityMutex);

    return retval;
}

void
Android_SetWindowTitle(_THIS, SDL_Window *window)
{
    Android_JNI_SetActivityTitle(window->title);
}

void
Android_SetWindowFullscreen(_THIS, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen)
{
    SDL_LockMutex(Android_ActivityMutex);

    if (window == Android_Window) {

        /* If the window is being destroyed don't change visible state */
        if (!window->is_destroying) {
            Android_JNI_SetWindowStyle(fullscreen);
        }

        /* Ensure our size matches reality after we've executed the window style change.
         *
         * It is possible that we've set width and height to the full-size display, but on
         * Samsung DeX or Chromebooks or other windowed Android environemtns, our window may
         * still not be the full display size.
         */
        if (!SDL_IsDeXMode() && !SDL_IsChromebook()) {
            goto endfunction;
        }

        SDL_WindowData *data = (SDL_WindowData *)window->driverdata;

        if (!data || !data->native_window) {
            if (data && !data->native_window) {
                SDL_SetError("Missing native window");
            }
            goto endfunction;
        }

        int old_w = window->w;
        int old_h = window->h;

        int new_w = ANativeWindow_getWidth(data->native_window);
        int new_h = ANativeWindow_getHeight(data->native_window);

        if (new_w < 0 || new_h < 0) {
            SDL_SetError("ANativeWindow_getWidth/Height() fails");
        }

        if (old_w != new_w || old_h != new_h) {
            SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, new_w, new_h);
        }
    }

endfunction:

    SDL_UnlockMutex(Android_ActivityMutex);
}

void
Android_MinimizeWindow(_THIS, SDL_Window *window)
{
    Android_JNI_MinizeWindow();
}

void
Android_DestroyWindow(_THIS, SDL_Window *window)
{
    SDL_LockMutex(Android_ActivityMutex);

    if (window == Android_Window) {
        Android_Window = NULL;

        if (window->driverdata) {
            SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
            if (data->egl_surface != EGL_NO_SURFACE) {
                SDL_EGL_DestroySurface(_this, data->egl_surface);
            }
            if (data->native_window) {
                ANativeWindow_release(data->native_window);
            }
            SDL_free(window->driverdata);
            window->driverdata = NULL;
        }
    }

    SDL_UnlockMutex(Android_ActivityMutex);
}

SDL_bool
Android_GetWindowWMInfo(_THIS, SDL_Window *window, SDL_SysWMinfo *info)
{
    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;

    if (info->version.major == SDL_MAJOR_VERSION &&
        info->version.minor == SDL_MINOR_VERSION) {
        info->subsystem = SDL_SYSWM_ANDROID;
        info->info.android.window = data->native_window;
        info->info.android.surface = data->egl_surface;
        return SDL_TRUE;
    } else {
        SDL_SetError("Application not compiled with SDL %d.%d",
                     SDL_MAJOR_VERSION, SDL_MINOR_VERSION);
        return SDL_FALSE;
    }
}

#endif /* SDL_VIDEO_DRIVER_ANDROID */

/* vi: set ts=4 sw=4 expandtab: */


text:
namespace $.$$ {
 	export class $mol_date extends $.$mol_date {

		@ $mol_mem
		value( val? : string ) {
			if( val === '' ) val = null

			const moment1 = $mol_try( ()=> val && new $mol_time_moment( val.replace( /-$/ , '' ) ) )
			if( moment1 instanceof Error ) return val

			const moment2 = this.value_moment( moment1 )
			return moment2 && moment2.toString( 'YYYY-MM-DD' )
		}

		@ $mol_mem
		value_moment( val? : $mol_time_moment ) {
			const stamp = this.value_number( val && val.valueOf() )
			return isNaN( stamp ) ? null : new $mol_time_moment( stamp )
		}

		showed( next? : boolean ) {
			const moment = $mol_try( ()=> new $mol_time_moment( this.value() ) )
			if( moment instanceof Error ) return false

			if( moment.year === undefined ) return false
			if( moment.month === undefined ) return false

			if( !this.focused( next ) ) return false

			return true
		}

		day_selected( day : string ) {
			return this.value() === day
		}

		day_click( day : string ) {
			this.value( day )
			this.showed( false )
		}

 	}

}


text:
package co.cask.cdap.client;

import co.cask.cdap.api.annotation.Beta;
import co.cask.cdap.api.data.schema.Schema;
import co.cask.cdap.client.config.ClientConfig;
import co.cask.cdap.client.util.RESTClient;
import co.cask.cdap.common.NotFoundException;
import co.cask.cdap.common.UnauthenticatedException;
import co.cask.cdap.internal.io.SchemaTypeAdapter;
import co.cask.cdap.proto.Id;
import co.cask.cdap.proto.ViewDetail;
import co.cask.cdap.proto.ViewSpecification;
import co.cask.cdap.security.spi.authorization.UnauthorizedException;
import co.cask.common.http.HttpMethod;
import co.cask.common.http.HttpRequest;
import co.cask.common.http.HttpResponse;
import co.cask.common.http.ObjectResponse;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
import javax.inject.Inject;

/**
 * Provides ways to interact with CDAP stream views.
 */
@Beta
public class StreamViewClient {

  private static final Gson GSON = new GsonBuilder()
    .registerTypeAdapter(Schema.class, new SchemaTypeAdapter())
    .create();
  private final RESTClient restClient;
  private final ClientConfig config;

  @Inject
  public StreamViewClient(ClientConfig config, RESTClient restClient) {
    this.config = config;
    this.restClient = restClient;
  }

  public StreamViewClient(ClientConfig config) {
    this(config, new RESTClient(config));
  }

  /**
   * Creates or updates a view.
   *
   * @param id the view
   * @param viewSpecification the view config
   * @return true if a view was created, false if updated
   */
  public boolean createOrUpdate(Id.Stream.View id, ViewSpecification viewSpecification)
    throws NotFoundException, IOException, UnauthenticatedException, UnauthorizedException {

    URL url = config.resolveNamespacedURLV3(
      id.getNamespace(), String.format("streams/%s/views/%s", id.getStreamId(), id.getId()));
    HttpRequest request = HttpRequest.put(url).withBody(GSON.toJson(viewSpecification)).build();
    HttpResponse response = restClient.execute(request, config.getAccessToken());
    return response.getResponseCode() == 201;
  }

  /**
   * Deletes a view.
   *
   * @param id the view
   * @throws NotFoundException if the view was not found
   */
  public void delete(Id.Stream.View id)
    throws IOException, UnauthenticatedException, NotFoundException, UnauthorizedException {
    URL url = config.resolveNamespacedURLV3(
      id.getNamespace(), String.format("streams/%s/views/%s", id.getStreamId(), id.getId()));
    HttpResponse response = restClient.execute(HttpMethod.DELETE, url, config.getAccessToken(),
                                               HttpURLConnection.HTTP_NOT_FOUND);
    if (response.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) {
      throw new NotFoundException(id.toEntityId());
    }
  }

  /**
   * Lists all views associated with a stream.
   *
   * @return the list of views associated with a stream
   */
  public List<String> list(Id.Stream stream) throws IOException, UnauthenticatedException, UnauthorizedException {
    URL url = config.resolveNamespacedURLV3(stream.getNamespace(), String.format("streams/%s/views", stream.getId()));
    HttpResponse response = restClient.execute(HttpMethod.GET, url, config.getAccessToken());
    return ObjectResponse.fromJsonBody(response, new TypeToken<List<String>>() { }).getResponseObject();
  }

  /**
   * Gets detailed information about a view.
   *
   * @param id the view
   * @return the detailed information about the view
   * @throws NotFoundException if the view was not found
   */
  public ViewDetail get(Id.Stream.View id)
    throws NotFoundException, IOException, UnauthenticatedException, UnauthorizedException {

    URL url = config.resolveNamespacedURLV3(
      id.getNamespace(), String.format("streams/%s/views/%s", id.getStreamId(), id.getId()));
    HttpResponse response = restClient.execute(HttpMethod.GET, url, config.getAccessToken(),
                                               HttpURLConnection.HTTP_NOT_FOUND);
    if (response.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) {
      throw new NotFoundException(id.toEntityId());
    }
    return ObjectResponse.fromJsonBody(response, ViewDetail.class, GSON).getResponseObject();
  }
}


text:
require_relative 'spec_helper'

describe 'kibana::install' do
  describe 'ubuntu' do
    let(:runner) { ChefSpec::Runner.new(::UBUNTU_OPTS) }
    let(:node) { runner.node }
    let(:chef_run) do
      # runner.node.set['logstash'] ...
      runner.node.set['kibana']['user'] = 'kibanana'
      runner.node.set['kibana']['install_dir'] = '/opt/kibanana'
      runner.node.set['kibana']['install_type'] = 'file'
      runner.node.set['kibana']['config_template'] = 'config.js.erb'
      runner.node.set['kibana']['config_cookbook'] = 'kibana'
      runner.node.set['kibana']['webserver'] = 'nginx'
      runner.node.set['kibana']['es_server'] = '127.0.0.1'
      runner.node.set['kibana']['nginx']['template'] = 'kibana-nginx.conf.erb'
      runner.node.set['kibana']['nginx']['template_cookbook'] = 'kibana'
      runner.node.set['kibana']['nginx']['enable_default_site'] = false
      runner.node.set['kibana']['nginx']['install_method'] = 'package'
      runner.converge(described_recipe)
    end
    include_context 'stubs-common'

    it 'creates kibana user' do
      expect(chef_run).to create_kibana_user('kibanana').with(
        user: 'kibanana',
        group: 'kibanana',
        home: '/opt/kibanana'
      )
    end

    it 'installs kibana' do
      expect(chef_run).to create_kibana_install('kibana').with(
        user: 'kibanana',
        group: 'kibanana',
        install_dir: '/opt/kibanana',
        install_type: 'file'
      )
    end

    it 'creates kibana config from template' do
      expect(chef_run).to create_template('/opt/kibanana/current/config.js').with(
        source: 'config.js.erb',
        cookbook: 'kibana',
        mode: '0750',
        user: 'kibanana'
      )
    end

    it 'links default logstash dashboard' do
      expect(chef_run).to create_link('/opt/kibanana/current/app/dashboards/default.json').with(
        to: 'logstash.json'
      )
    end

    it 'installs and configures a webserver' do
      expect(chef_run).to create_kibana_web('kibana').with(
        type: 'nginx',
        docroot: '/opt/kibanana/current',
        es_server: '127.0.0.1'
      )
    end

  end
end


text:
layout:     post
title:      I f***ing did it
date:       2014-08-31 13:40:00
summary:    I end up with a blog after being challenged to have my web site finally up and running in two weeks
categories: blog
---

## Go fucking do it?

I have been talking about having my web site/blog it's been months, may be years, and now here it is!

I think the last drop in my procrastination bucket was when I accepted [@mauehara](https://twitter.com/mauehara)'s challenge on [gofuckingdoit.com](http://gofuckingdoit.com), meaning that I would have to pay cash $$ if I didn't have my website (with an [*about me*](http://felipe.sabino.me/about/) page) up and running within 2 weeks and well, [it worked!](https://gofuckingdoit.com/result/9db76259bb442fd0/)

Geeky as I am, I decided to share some technicalities about why I chose [Jekyll](jekyllrb.com) and end up running this page for free on [Github](https://github.com).

## Jekyll and Github

I am so surprised to see how technically simple it was have this blog up and running, but how hard it was to fill it up with (at least some) content.

After some basic research trying to find the easiest\*, fastest and cheapest way possible to have a blog system, I end up sticking with the [Jekyll](jekyllrb.com) + [Github](https://github.com) solution. If you thinking about having your own blog, here are some thoughts on why I chose Jekyll and Github:

### Static content only

Yap, you read it, no server code, no databases, HTML only, totally static.

One point is that I have to deal with servers, scalability, databases, performance, cache, etc etc etc every single day and one thing I really wanted was to avoid this kind of headache. Not only that, HTML files you can host anywhere and you don't even need anything special about the server, I could put these HTML files in my dropbox\*\* folder if I wanted and everything would still work... it would be a bit slow, I know, but it would work and be *good enough*.

### Git/Github based

Jekyll is the engine that runs [Github Pages](https://pages.github.com) whih makes it [very simple to have you blog running on github](https://help.github.com/articles/using-jekyll-with-pages), with minimal setup possible. If the [github tutorial](https://help.github.com/articles/using-jekyll-with-pages) is not enought, [Matt West has a very detailed blog post explaining how to set up github pages and jekyll](http://blog.teamtreehouse.com/using-github-pages-to-host-your-website), also including details about the domain/subdomain setup that you would have to dig through Github's documentation.

Not only that, it makes easy to collaborate, If you find a bug or something that is not up-to-date, just go [the repository](https://github.com/felipesabino/felipesabino.github.io) and create an issue, or fix it and send me a pull request.

Pull requests are prettu handy, and it is something that I really wanted since the beggining. I got frustrated several times in the past about not being able to help [fix the internet](https://xkcd.com/386/), like one time the [objc.io #6](http://www.objc.io/issue-6/travis-ci.html) blog and was [asked to write an email with the issues I found](https://twitter.com/MattesGroeger/statuses/423221404369055744), nah..

### Markdown out of the box

Since I met [Haml](http://haml.info/) (back on the days), I just could never get used again to the plain old way of writting HTML tags, they seem so confusing and it is so easy to forget to close one and have a bad formatted document, so whenever I have the opportunity, I choose not to use it, opting for alternatives, like [Jade](http://jade-lang.com/) for Node.js based systems, for example.

Besides that, this is a blog, so it is supposed to be text oriented and I don't want to keep worrying about opening paragraph and image tags, I just want to focus on the content, which is something [markdown](http://daringfireball.net/projects/markdown/) is pretty good at and Jekyyl has as default.

## Alternatives

I also considered some other blog platforms before sticking to this one that might be suitable for you.

### Ghost

When [Ghost](https://ghost.org/) first [launched to the public](http://blog.ghost.org/public-launch/) almost an year ago I though it was an awesome idea, simple, no comments and open source.

The only downside for using Ghost was having to host it somewhere and (as I explained here already) this wasn't something I was willing to do, also I didn't want to [pay for the hosted solution](https://ghost.org/pricing/) as well, so I kept ghost as I backup in case I didn't have any other good options

### Medium

[Medium](https://medium.com/) was also an alternative but the problem is that I wanted to have a page of my own, my custom content, being able to add more stuff in the future (not that I would do it, but I want the option at least) and also be able to change style freely, so there you go, Medium was also not an option.

---

\* I know that easiest might not be a suitable term for everybody, my mom for example would surely find it impossible to do, or even start doing, but any developer should be able to do it just by reading github docs :p

\*\* Yes, I am aware of all the [crazy debate going on in the internet since Condoleezza Rice joined Dropbox's Board of Directors](http://www.drop-dropbox.com/), but you know what I meant, right?


text:
define("ghost/routes/debug",
  ["exports"],
  function(__exports__) {
    "use strict";
    var DebugRoute = Ember.Route.extend({
        beforeModel: function () {
            this.transitionTo('settings.labs');
        }
    });

    __exports__["default"] = DebugRoute;
  });


text:
from __future__ import absolute_import
from tvtk.view.parametric_function_source_view import *


text:
/*
 * program_05_08.c
 *
 * Program to evaluate simple expressions of the form "number operator number"
 */

#include <stdio.h>

int main (void)
{
	float value1, value2;
	char operator;

	printf ("Type in your expression.\n");
	scanf ("%f %c %f", &value1, &operator, &value2);

	if (operator == '+')
		printf ("%.2f\n", value1 + value2);
	else if (operator == '-')
		printf ("%.2f\n", value1 - value2);
	else if (operator == '*')
		printf ("%.2f\n", value1 * value2);
	else if (operator == '/')
		printf ("%.2f\n", value1 / value2);

	return 0;
}


text:
The App class is the central class for the core and holds the registry of objects used throughout the system.

As the framework handles the startup process the main functions of use will be those relating to using the registry and also addon checks.

## Adding to Registry

Use the following function to add an object to the registry:

    /**
     * add item to the registry
     *
     * @static
     * @param string - identifying key for retrieval later on
     * @param mixed - item to add to the registry
     * @param bool - overwrite item if key is already taken? (optional:default = true)
     * @return bool - usually true (unless $overwrite = false and $key already exists or $key is empty)
     */
    public static function add($key, $item, $overwrite=true)

This can be used in the follow way:

	$my_class = new \Vendor\MyClass($my_var);
	\App::add('myclass', $my_class);

As default, this will obviously overwrite any object already stored with the key of 'myClass', simple set the third parameter to false if you wish to protect objects. The function will return **false** should an object with that key already exist.

### Object initiate and add

The App class also provides a factory function which will start up any object with any passed parameters and then add it to the registry.

    /**
     * load the given class and add to App
     * will be added with the key of lowercase classname
     *
     * @param   string  class to load (including namespaces)
     * @param   mixed   optional data to pass to class constructor
     * @return  mixed   bool on false, object on load
     */
    public static function factory()

Using the same example with a our MyClass object, you would be able to use the factory in the following way:

	\App::factory('\Vendor\MyClass', $my_var);

The class name is used as the key for the registry and is turned to lowercase before adding, so it will be the exact same key as the above example when using standard **add** method.

## Retrieving from Registry

To retrieve an object from the registry you use the simple "getter".

    /**
     * retrieve an object from the registry
     *
     * @static
     * @param string - key to find and retrieve
     * @return mixed - the item stored / null if it's not set
     */
    public static function &get($key)

The key to be passed, is the key specified within the add or the class name when using the factory method.

	$my_class = \App::get('myclass');

The getter returns the objects as reference, allowing you to
a) chain methods straight from the getter
b) affect the object without having to re-add to the registry.

	$my_class = new MyClass();
	$my_class->setFoo('bar');

	\App::add('myclass', $my_class);

	echo \App::get('myclass')->getFoo(); // will return 'bar'

	\App::get('myclass')->setFoo('foo-bar');

	echo \App::get('myclass')->getFoo(); // will return 'foo-bar'

### Checking the Registry

The App class also provides the ability to check whether an item exists in the registry and also provides the ability to check which addons are currently installed.

The check returns a simple true / false boolean value as to whether or not the item exists within the registry with the given key.

    /**
     * check to see if an object exists in the registry
     *
     * @static
     * @param string - key to find and retrieve
     * @return bool
     */
    public static function check($key)

Using the **myclass** example above you can check to see if it is in the registry like so:

	$exists = \App::check('myclass');

To check the App registry for installed addons use the following function

    /**
     * check whether addon is installed
     *
     * @param   string  addon name
     * @return  bool    whether addon is installed
     */
    public static function checkInstalledAddon($addon_name)

The function also returns a true / false value whether it was loaded during start up.

	$exists = \App::checkInstalledAddon('support_desk');


text:
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include "cgpt.h"
#include "cgptlib_internal.h"
#include "crc32.h"
#include "flash_ts.h"
#include "flash_ts_api.h"
#include "vboot_host.h"

struct nand_layout nand;

void EnableNandImage(int bytes_per_page, int pages_per_block,
                     int fts_block_offset, int fts_block_size) {
  nand.enabled = 1;
  nand.bytes_per_page = bytes_per_page;
  nand.pages_per_block = pages_per_block;
  nand.fts_block_offset = fts_block_offset;
  nand.fts_block_size = fts_block_size;
}

void Error(const char *format, ...) {
  va_list ap;
  va_start(ap, format);
  fprintf(stderr, "ERROR: %s %s: ", progname, command);
  vfprintf(stderr, format, ap);
  va_end(ap);
}

int CheckValid(const struct drive *drive) {
  if ((drive->gpt.valid_headers != MASK_BOTH) ||
      (drive->gpt.valid_entries != MASK_BOTH)) {
    fprintf(stderr, "\nWARNING: one of the GPT header/entries is invalid, "
           "please run '%s repair'\n", progname);
    return CGPT_FAILED;
  }
  return CGPT_OK;
}

int Load(struct drive *drive, uint8_t **buf,
                const uint64_t sector,
                const uint64_t sector_bytes,
                const uint64_t sector_count) {
  int count;  /* byte count to read */
  int nread;
  int fd = drive->fd;

  require(buf);
  if (!sector_count || !sector_bytes) {
    Error("%s() failed at line %d: sector_count=%d, sector_bytes=%d\n",
          __FUNCTION__, __LINE__, sector_count, sector_bytes);
    return CGPT_FAILED;
  }
  /* Make sure that sector_bytes * sector_count doesn't roll over. */
  if (sector_bytes > (UINT64_MAX / sector_count)) {
    Error("%s() failed at line %d: sector_count=%d, sector_bytes=%d\n",
          __FUNCTION__, __LINE__, sector_count, sector_bytes);
    return CGPT_FAILED;
  }
  count = sector_bytes * sector_count;
  *buf = malloc(count);
  require(*buf);

  if (-1 == lseek(fd, sector * sector_bytes, SEEK_SET)) {
    Error("Can't lseek: %s\n", strerror(errno));
    goto error_free;
  }

  nread = read(fd, *buf, count);
  if (nread < count) {
    Error("Can't read enough: %d, not %d\n", nread, count);
    goto error_free;
  }

  return CGPT_OK;

error_free:
  free(*buf);
  *buf = 0;
  return CGPT_FAILED;
}

int ReadPMBR(struct drive *drive) {
  if (-1 == lseek(drive->fd, 0, SEEK_SET))
    return CGPT_FAILED;

  int nread = read(drive->fd, &drive->pmbr, sizeof(struct pmbr));
  if (nread != sizeof(struct pmbr))
    return CGPT_FAILED;

  return CGPT_OK;
}

int WritePMBR(struct drive *drive) {
  if (-1 == lseek(drive->fd, 0, SEEK_SET))
    return CGPT_FAILED;

  int nwrote = write(drive->fd, &drive->pmbr, sizeof(struct pmbr));
  if (nwrote != sizeof(struct pmbr))
    return CGPT_FAILED;

  return CGPT_OK;
}

int Save(struct drive *drive, const uint8_t *buf,
                const uint64_t sector,
                const uint64_t sector_bytes,
                const uint64_t sector_count) {
  int count;  /* byte count to write */
  int nwrote;
  int fd = drive->fd;

  require(buf);
  count = sector_bytes * sector_count;

  if (-1 == lseek(fd, sector * sector_bytes, SEEK_SET))
    return CGPT_FAILED;

  nwrote = write(fd, buf, count);
  if (nwrote < count)
    return CGPT_FAILED;

  return CGPT_OK;
}

static int get_hex_char_value(char ch) {
  if (ch >= '0' && ch <= '9') {
    return ch - '0';
  }
  if (ch >= 'a' && ch <= 'f') {
    return ch - 'a' + 10;
  }
  if (ch >= 'A' && ch <= 'F') {
    return ch - 'A' + 10;
  }
  return -1;
}

int TryInitMtd(const char *dev) {
  static int already_inited = 0;
  if (already_inited)
    return nand.use_host_ioctl;

  already_inited = 1;

  /* If we're running on the live system, we can just use /dev/fts and not
   * actually need the specific parameters. This needs to be accessed via
   * ioctl and not normal I/O.
   */
  if (!strcmp(dev, FTS_DEVICE) && !access(FTS_DEVICE, R_OK | W_OK)) {
    nand.enabled = 1;
    nand.use_host_ioctl = 1;
    return 1;
  }
  return 0;
}

int FlashGet(const char *key, uint8_t *data, uint32_t *bufsz) {
  char *hex = (char*)malloc(*bufsz * 2);
  char *read;
  uint32_t written = 0;

  if (nand.use_host_ioctl) {
    struct flash_ts_io_req req;
    strncpy(req.key, key, sizeof(req.key));
    int fd = open(FTS_DEVICE, O_RDWR);
    if (fd < 0)
      return -1;
    if (ioctl(fd, FLASH_TS_IO_GET, &req))
      return -1;
    strncpy(hex, req.val, *bufsz * 2);
    close(fd);
  } else {
    flash_ts_get(key, hex, *bufsz * 2);
  }

  /* Hex -> binary */
  for (read = hex; read < hex + *bufsz * 2 && *read != '\0'; read += 2) {
    int c0, c1;
    c0 = get_hex_char_value(read[0]);
    c1 = get_hex_char_value(read[1]);
    if (c0 < 0 || c1 < 0) {
      free(hex);
      return -1;
    }

    data[written++] = (c0 << 4) + c1;
  }
  *bufsz = written;
  free(hex);
  return 0;
}

int FlashSet(const char *key, const uint8_t *data, uint32_t bufsz) {
  char *hex = (char*)malloc(bufsz * 2 + 1);
  const char *hex_chars = "0123456789ABCDEF";
  int ret;
  uint32_t i;

  /* Binary -> hex, we need some encoding because FTS only stores C strings */
  for (i = 0; i < bufsz; i++) {
    hex[i * 2] = hex_chars[data[i] >> 4];
    hex[i * 2 + 1] = hex_chars[data[i] & 0xF];
  }
  /* Buffer must be NUL-terminated. */
  hex[bufsz * 2] = '\0';
  if (nand.use_host_ioctl) {
    struct flash_ts_io_req req;
    strncpy(req.key, key, sizeof(req.key));
    strncpy(req.val, hex, sizeof(req.val));
    free(hex);
    int fd = open(FTS_DEVICE, O_RDWR);
    if (fd < 0)
      return -1;
    if (ioctl(fd, FLASH_TS_IO_SET, &req))
      return -1;
    close(fd);
    return 0;
  }
  ret = flash_ts_set(key, hex);
  free(hex);
  return ret;
}

int MtdLoad(struct drive *drive, int sector_bytes) {
  int ret;
  uint32_t sz;
  MtdData *mtd = &drive->mtd;

  mtd->sector_bytes = sector_bytes;
  mtd->drive_sectors = drive->size / mtd->sector_bytes;

  if (!nand.use_host_ioctl) {
    ret = flash_ts_init(mtd->fts_block_offset,
                        mtd->fts_block_size,
                        mtd->flash_page_bytes,
                        mtd->flash_block_bytes,
                        mtd->sector_bytes, /* Needed for Load() and Save() */
                        drive);
    if (ret)
      return ret;
  }

  memset(&mtd->primary, 0, sizeof(mtd->primary));
  sz = sizeof(mtd->primary);
  ret = FlashGet(MTD_DRIVE_SIGNATURE, (uint8_t *)&mtd->primary, &sz);
  if (ret)
    return ret;

  /* Read less than expected */
  if (sz < MTD_DRIVE_V1_SIZE)
    memset(&mtd->primary, 0, sizeof(mtd->primary));

  if (nand.use_host_ioctl) {
    /* If we are using /dev/fts, we can't stat() the size, so re-use
     * our internal value to set it.
     */
    drive->size = mtd->primary.last_offset + 1;
    mtd->drive_sectors = drive->size / mtd->sector_bytes;
  }

  mtd->current_kernel = -1;
  mtd->current_priority = 0;
  mtd->modified = 0;
  return 0;
}

int MtdSave(struct drive *drive) {
  MtdData *mtd = &drive->mtd;

  if (!mtd->modified)
    return 0;

  mtd->primary.crc32 = 0;
  mtd->primary.crc32 = Crc32(&mtd->primary, MTD_DRIVE_V1_SIZE);

  return FlashSet(MTD_DRIVE_SIGNATURE, (uint8_t *)&mtd->primary,
                  sizeof(mtd->primary));
}

int GptLoad(struct drive *drive, uint32_t sector_bytes) {
  drive->gpt.sector_bytes = sector_bytes;
  if (drive->size % drive->gpt.sector_bytes) {
    Error("Media size (%llu) is not a multiple of sector size(%d)\n",
          (long long unsigned int)drive->size, drive->gpt.sector_bytes);
    return -1;
  }
  drive->gpt.drive_sectors = drive->size / drive->gpt.sector_bytes;

  // Read the data.
  if (CGPT_OK != Load(drive, &drive->gpt.primary_header,
                      GPT_PMBR_SECTOR,
                      drive->gpt.sector_bytes, GPT_HEADER_SECTOR)) {
    return -1;
  }
  if (CGPT_OK != Load(drive, &drive->gpt.secondary_header,
                      drive->gpt.drive_sectors - GPT_PMBR_SECTOR,
                      drive->gpt.sector_bytes, GPT_HEADER_SECTOR)) {
    return -1;
  }
  if (CGPT_OK != Load(drive, &drive->gpt.primary_entries,
                      GPT_PMBR_SECTOR + GPT_HEADER_SECTOR,
                      drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS)) {
    return -1;
  }
  if (CGPT_OK != Load(drive, &drive->gpt.secondary_entries,
                      drive->gpt.drive_sectors - GPT_HEADER_SECTOR
                      - GPT_ENTRIES_SECTORS,
                      drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS)) {
    return -1;
  }
  return 0;
}

int GptSave(struct drive *drive) {
  int errors = 0;
  if (drive->gpt.modified & GPT_MODIFIED_HEADER1) {
    if (CGPT_OK != Save(drive, drive->gpt.primary_header,
                        GPT_PMBR_SECTOR,
                        drive->gpt.sector_bytes, GPT_HEADER_SECTOR)) {
      errors++;
      Error("Cannot write primary header: %s\n", strerror(errno));
    }
  }

  if (drive->gpt.modified & GPT_MODIFIED_HEADER2) {
    if(CGPT_OK != Save(drive, drive->gpt.secondary_header,
                       drive->gpt.drive_sectors - GPT_PMBR_SECTOR,
                       drive->gpt.sector_bytes, GPT_HEADER_SECTOR)) {
      errors++;
      Error("Cannot write secondary header: %s\n", strerror(errno));
    }
  }
  if (drive->gpt.modified & GPT_MODIFIED_ENTRIES1) {
    if (CGPT_OK != Save(drive, drive->gpt.primary_entries,
                        GPT_PMBR_SECTOR + GPT_HEADER_SECTOR,
                        drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS)) {
      errors++;
      Error("Cannot write primary entries: %s\n", strerror(errno));
    }
  }
  if (drive->gpt.modified & GPT_MODIFIED_ENTRIES2) {
    if (CGPT_OK != Save(drive, drive->gpt.secondary_entries,
                        drive->gpt.drive_sectors - GPT_HEADER_SECTOR
                        - GPT_ENTRIES_SECTORS,
                        drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS)) {
      errors++;
      Error("Cannot write secondary entries: %s\n", strerror(errno));
    }
  }

  if (drive->gpt.primary_header)
    free(drive->gpt.primary_header);
  drive->gpt.primary_header = 0;
  if (drive->gpt.primary_entries)
    free(drive->gpt.primary_entries);
  drive->gpt.primary_entries = 0;
  if (drive->gpt.secondary_header)
    free(drive->gpt.secondary_header);
  drive->gpt.secondary_header = 0;
  if (drive->gpt.secondary_entries)
    free(drive->gpt.secondary_entries);
  drive->gpt.secondary_entries = 0;
  return errors ? -1 : 0;
}

// Opens a block device or file, loads raw GPT data from it.
// mode should be O_RDONLY or O_RDWR
//
// Returns CGPT_FAILED if any error happens.
// Returns CGPT_OK if success and information are stored in 'drive'. */
int DriveOpen(const char *drive_path, struct drive *drive, int mode) {
  struct stat stat;
  uint32_t sector_bytes;
  int is_mtd = nand.enabled;

  require(drive_path);
  require(drive);

  // Clear struct for proper error handling.
  memset(drive, 0, sizeof(struct drive));

  if (TryInitMtd(drive_path)) {
    is_mtd = 1;
    sector_bytes = 512;  /* bytes */
  } else {
    drive->fd = open(drive_path, mode | O_LARGEFILE | O_NOFOLLOW);
    if (drive->fd == -1) {
      Error("Can't open %s: %s\n", drive_path, strerror(errno));
      return CGPT_FAILED;
    }

    if (fstat(drive->fd, &stat) == -1) {
      Error("Can't fstat %s: %s\n", drive_path, strerror(errno));
      goto error_close;
    }
    if ((stat.st_mode & S_IFMT) != S_IFREG) {
      if (ioctl(drive->fd, BLKGETSIZE64, &drive->size) < 0) {
        Error("Can't read drive size from %s: %s\n", drive_path,
              strerror(errno));
        goto error_close;
      }
      if (ioctl(drive->fd, BLKSSZGET, &sector_bytes) < 0) {
        Error("Can't read sector size from %s: %s\n",
              drive_path, strerror(errno));
        goto error_close;
      }
    } else {
      sector_bytes = 512;  /* bytes */
      drive->size = stat.st_size;
    }
  }
  drive->is_mtd = is_mtd;

  if (is_mtd) {
    drive->mtd.fts_block_offset = nand.fts_block_offset;
    drive->mtd.fts_block_size = nand.fts_block_size;
    drive->mtd.flash_page_bytes = nand.bytes_per_page;
    drive->mtd.flash_block_bytes = nand.pages_per_block * nand.bytes_per_page;
    if (MtdLoad(drive, sector_bytes)) {
      goto error_close;
    }
  } else {
    if (GptLoad(drive, sector_bytes)) {
      goto error_close;
    }
  }

  // We just load the data. Caller must validate it.
  return CGPT_OK;

error_close:
  (void) DriveClose(drive, 0);
  return CGPT_FAILED;
}

int DriveClose(struct drive *drive, int update_as_needed) {
  int errors = 0;

  if (update_as_needed) {
    if (drive->is_mtd) {
      if (MtdSave(drive)) {
        errors++;
      }
    } else {
      if (GptSave(drive)) {
        errors++;
      }
    }
  }

  // Sync early! Only sync file descriptor here, and leave the whole system sync
  // outside cgpt because whole system sync would trigger tons of disk accesses
  // and timeout tests.
  fsync(drive->fd);

  close(drive->fd);

  return errors ? CGPT_FAILED : CGPT_OK;
}

/* GUID conversion functions. Accepted format:
 *
 *   "C12A7328-F81F-11D2-BA4B-00A0C93EC93B"
 *
 * Returns CGPT_OK if parsing is successful; otherwise CGPT_FAILED.
 */
int StrToGuid(const char *str, Guid *guid) {
  uint32_t time_low;
  uint16_t time_mid;
  uint16_t time_high_and_version;
  unsigned int chunk[11];

  if (11 != sscanf(str, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
                   chunk+0,
                   chunk+1,
                   chunk+2,
                   chunk+3,
                   chunk+4,
                   chunk+5,
                   chunk+6,
                   chunk+7,
                   chunk+8,
                   chunk+9,
                   chunk+10)) {
    printf("FAILED\n");
    return CGPT_FAILED;
  }

  time_low = chunk[0] & 0xffffffff;
  time_mid = chunk[1] & 0xffff;
  time_high_and_version = chunk[2] & 0xffff;

  guid->u.Uuid.time_low = htole32(time_low);
  guid->u.Uuid.time_mid = htole16(time_mid);
  guid->u.Uuid.time_high_and_version = htole16(time_high_and_version);

  guid->u.Uuid.clock_seq_high_and_reserved = chunk[3] & 0xff;
  guid->u.Uuid.clock_seq_low = chunk[4] & 0xff;
  guid->u.Uuid.node[0] = chunk[5] & 0xff;
  guid->u.Uuid.node[1] = chunk[6] & 0xff;
  guid->u.Uuid.node[2] = chunk[7] & 0xff;
  guid->u.Uuid.node[3] = chunk[8] & 0xff;
  guid->u.Uuid.node[4] = chunk[9] & 0xff;
  guid->u.Uuid.node[5] = chunk[10] & 0xff;

  return CGPT_OK;
}
void GuidToStr(const Guid *guid, char *str, unsigned int buflen) {
  require(buflen >= GUID_STRLEN);
  require(snprintf(str, buflen,
                  "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
                  le32toh(guid->u.Uuid.time_low),
                  le16toh(guid->u.Uuid.time_mid),
                  le16toh(guid->u.Uuid.time_high_and_version),
                  guid->u.Uuid.clock_seq_high_and_reserved,
                  guid->u.Uuid.clock_seq_low,
                  guid->u.Uuid.node[0], guid->u.Uuid.node[1],
                  guid->u.Uuid.node[2], guid->u.Uuid.node[3],
                  guid->u.Uuid.node[4], guid->u.Uuid.node[5]) == GUID_STRLEN-1);
}

/* Convert possibly unterminated UTF16 string to UTF8.
 * Caller must prepare enough space for UTF8, which could be up to
 * twice the byte length of UTF16 string plus the terminating '\0'.
 * See the following table for encoding lengths.
 *
 *     Code point       UTF16       UTF8
 *   0x0000-0x007F     2 bytes     1 byte
 *   0x0080-0x07FF     2 bytes     2 bytes
 *   0x0800-0xFFFF     2 bytes     3 bytes
 *  0x10000-0x10FFFF   4 bytes     4 bytes
 *
 * This function uses a simple state meachine to convert UTF-16 char(s) to
 * a code point. Once a code point is parsed out, the state machine throws
 * out sequencial UTF-8 chars in one time.
 *
 * Return: CGPT_OK --- all character are converted successfully.
 *         CGPT_FAILED --- convert error, i.e. output buffer is too short.
 */
int UTF16ToUTF8(const uint16_t *utf16, unsigned int maxinput,
                uint8_t *utf8, unsigned int maxoutput)
{
  size_t s16idx, s8idx;
  uint32_t code_point = 0;
  int code_point_ready = 1;  // code point is ready to output.
  int retval = CGPT_OK;

  if (!utf16 || !maxinput || !utf8 || !maxoutput)
    return CGPT_FAILED;

  maxoutput--;                             /* plan for termination now */

  for (s16idx = s8idx = 0;
       s16idx < maxinput && utf16[s16idx] && maxoutput;
       s16idx++) {
    uint16_t codeunit = le16toh(utf16[s16idx]);

    if (code_point_ready) {
      if (codeunit >= 0xD800 && codeunit <= 0xDBFF) {
        /* high surrogate, need the low surrogate. */
        code_point_ready = 0;
        code_point = (codeunit & 0x03FF) + 0x0040;
      } else {
        /* BMP char, output it. */
        code_point = codeunit;
      }
    } else {
      /* expect the low surrogate */
      if (codeunit >= 0xDC00 && codeunit <= 0xDFFF) {
        code_point = (code_point << 10) | (codeunit & 0x03FF);
        code_point_ready = 1;
      } else {
        /* the second code unit is NOT the low surrogate. Unexpected. */
        code_point_ready = 0;
        retval = CGPT_FAILED;
        break;
      }
    }

    /* If UTF code point is ready, output it. */
    if (code_point_ready) {
      require(code_point <= 0x10FFFF);
      if (code_point <= 0x7F && maxoutput >= 1) {
        maxoutput -= 1;
        utf8[s8idx++] = code_point & 0x7F;
      } else if (code_point <= 0x7FF && maxoutput >= 2) {
        maxoutput -= 2;
        utf8[s8idx++] = 0xC0 | (code_point >> 6);
        utf8[s8idx++] = 0x80 | (code_point & 0x3F);
      } else if (code_point <= 0xFFFF && maxoutput >= 3) {
        maxoutput -= 3;
        utf8[s8idx++] = 0xE0 | (code_point >> 12);
        utf8[s8idx++] = 0x80 | ((code_point >> 6) & 0x3F);
        utf8[s8idx++] = 0x80 | (code_point & 0x3F);
      } else if (code_point <= 0x10FFFF && maxoutput >= 4) {
        maxoutput -= 4;
        utf8[s8idx++] = 0xF0 | (code_point >> 18);
        utf8[s8idx++] = 0x80 | ((code_point >> 12) & 0x3F);
        utf8[s8idx++] = 0x80 | ((code_point >> 6) & 0x3F);
        utf8[s8idx++] = 0x80 | (code_point & 0x3F);
      } else {
        /* buffer underrun */
        retval = CGPT_FAILED;
        break;
      }
    }
  }
  utf8[s8idx++] = 0;
  return retval;
}

/* Convert UTF8 string to UTF16. The UTF8 string must be null-terminated.
 * Caller must prepare enough space for UTF16, including a terminating 0x0000.
 * See the following table for encoding lengths. In any case, the caller
 * just needs to prepare the byte length of UTF8 plus the terminating 0x0000.
 *
 *     Code point       UTF16       UTF8
 *   0x0000-0x007F     2 bytes     1 byte
 *   0x0080-0x07FF     2 bytes     2 bytes
 *   0x0800-0xFFFF     2 bytes     3 bytes
 *  0x10000-0x10FFFF   4 bytes     4 bytes
 *
 * This function converts UTF8 chars to a code point first. Then, convrts it
 * to UTF16 code unit(s).
 *
 * Return: CGPT_OK --- all character are converted successfully.
 *         CGPT_FAILED --- convert error, i.e. output buffer is too short.
 */
int UTF8ToUTF16(const uint8_t *utf8, uint16_t *utf16, unsigned int maxoutput)
{
  size_t s16idx, s8idx;
  uint32_t code_point = 0;
  unsigned int expected_units = 1;
  unsigned int decoded_units = 1;
  int retval = CGPT_OK;

  if (!utf8 || !utf16 || !maxoutput)
    return CGPT_FAILED;

  maxoutput--;                             /* plan for termination */

  for (s8idx = s16idx = 0;
       utf8[s8idx] && maxoutput;
       s8idx++) {
    uint8_t code_unit;
    code_unit = utf8[s8idx];

    if (expected_units != decoded_units) {
      /* Trailing bytes of multi-byte character */
      if ((code_unit & 0xC0) == 0x80) {
        code_point = (code_point << 6) | (code_unit & 0x3F);
        ++decoded_units;
      } else {
        /* Unexpected code unit. */
        retval = CGPT_FAILED;
        break;
      }
    } else {
      /* parsing a new code point. */
      decoded_units = 1;
      if (code_unit <= 0x7F) {
        code_point = code_unit;
        expected_units = 1;
      } else if (code_unit <= 0xBF) {
        /* 0x80-0xBF must NOT be the heading byte unit of a new code point. */
        retval = CGPT_FAILED;
        break;
      } else if (code_unit >= 0xC2 && code_unit <= 0xDF) {
        code_point = code_unit & 0x1F;
        expected_units = 2;
      } else if (code_unit >= 0xE0 && code_unit <= 0xEF) {
        code_point = code_unit & 0x0F;
        expected_units = 3;
      } else if (code_unit >= 0xF0 && code_unit <= 0xF4) {
        code_point = code_unit & 0x07;
        expected_units = 4;
      } else {
        /* illegal code unit: 0xC0-0xC1, 0xF5-0xFF */
        retval = CGPT_FAILED;
        break;
      }
    }

    /* If no more unit is needed, output the UTF16 unit(s). */
    if ((retval == CGPT_OK) &&
        (expected_units == decoded_units)) {
      /* Check if the encoding is the shortest possible UTF-8 sequence. */
      switch (expected_units) {
        case 2:
          if (code_point <= 0x7F) retval = CGPT_FAILED;
          break;
        case 3:
          if (code_point <= 0x7FF) retval = CGPT_FAILED;
          break;
        case 4:
          if (code_point <= 0xFFFF) retval = CGPT_FAILED;
          break;
      }
      if (retval == CGPT_FAILED) break;  /* leave immediately */

      if ((code_point <= 0xD7FF) ||
          (code_point >= 0xE000 && code_point <= 0xFFFF)) {
        utf16[s16idx++] = code_point;
        maxoutput -= 1;
      } else if (code_point >= 0x10000 && code_point <= 0x10FFFF &&
                 maxoutput >= 2) {
        utf16[s16idx++] = 0xD800 | ((code_point >> 10) - 0x0040);
        utf16[s16idx++] = 0xDC00 | (code_point & 0x03FF);
        maxoutput -= 2;
      } else {
        /* Three possibilities fall into here. Both are failure cases.
         *   a. surrogate pair (non-BMP characters; 0xD800~0xDFFF)
         *   b. invalid code point > 0x10FFFF
         *   c. buffer underrun
         */
        retval = CGPT_FAILED;
        break;
      }
    }
  }

  /* A null-terminator shows up before the UTF8 sequence ends. */
  if (expected_units != decoded_units) {
    retval = CGPT_FAILED;
  }

  utf16[s16idx++] = 0;
  return retval;
}

/* global types to compare against */
const Guid guid_chromeos_firmware = GPT_ENT_TYPE_CHROMEOS_FIRMWARE;
const Guid guid_chromeos_kernel =   GPT_ENT_TYPE_CHROMEOS_KERNEL;
const Guid guid_chromeos_rootfs =   GPT_ENT_TYPE_CHROMEOS_ROOTFS;
const Guid guid_linux_data =        GPT_ENT_TYPE_LINUX_DATA;
const Guid guid_chromeos_reserved = GPT_ENT_TYPE_CHROMEOS_RESERVED;
const Guid guid_efi =               GPT_ENT_TYPE_EFI;
const Guid guid_unused =            GPT_ENT_TYPE_UNUSED;

const static struct {
  const Guid *type;
  char *name;
  char *description;
  int mtd_type;
} supported_types[] = {
  {&guid_chromeos_firmware, "firmware", "ChromeOS firmware",
    MTD_PARTITION_TYPE_CHROMEOS_FIRMWARE},
  {&guid_chromeos_kernel, "kernel", "ChromeOS kernel",
    MTD_PARTITION_TYPE_CHROMEOS_KERNEL},
  {&guid_chromeos_rootfs, "rootfs", "ChromeOS rootfs",
    MTD_PARTITION_TYPE_CHROMEOS_ROOTFS},
  {&guid_linux_data, "data", "Linux data",
    MTD_PARTITION_TYPE_LINUX_DATA},
  {&guid_chromeos_reserved, "reserved", "ChromeOS reserved",
    MTD_PARTITION_TYPE_CHROMEOS_RESERVED},
  {&guid_efi, "efi", "EFI System Partition",
    MTD_PARTITION_TYPE_EFI},
  {&guid_unused, "unused", "Unused (nonexistent) partition",
    MTD_PARTITION_TYPE_UNUSED},
};

int LookupMtdTypeForGuid(const Guid *type) {
  int i;
  for (i = 0; i < ARRAY_COUNT(supported_types); ++i) {
    if (!memcmp(type, supported_types[i].type, sizeof(Guid))) {
      return supported_types[i].mtd_type;
    }
  }
  return MTD_PARTITION_TYPE_OTHER;
}

const Guid *LookupGuidForMtdType(int type) {
  int i;
  for (i = 0; i < ARRAY_COUNT(supported_types); ++i) {
    if (supported_types[i].mtd_type == type) {
      return supported_types[i].type;
    }
  }
  return NULL;
}

/* Resolves human-readable GPT type.
 * Returns CGPT_OK if found.
 * Returns CGPT_FAILED if no known type found. */
int ResolveType(const Guid *type, char *buf) {
  int i;
  for (i = 0; i < ARRAY_COUNT(supported_types); ++i) {
    if (!memcmp(type, supported_types[i].type, sizeof(Guid))) {
      strcpy(buf, supported_types[i].description);
      return CGPT_OK;
    }
  }
  return CGPT_FAILED;
}

int SupportedType(const char *name, Guid *type) {
  int i;
  for (i = 0; i < ARRAY_COUNT(supported_types); ++i) {
    if (!strcmp(name, supported_types[i].name)) {
      memcpy(type, supported_types[i].type, sizeof(Guid));
      return CGPT_OK;
    }
  }
  return CGPT_FAILED;
}

void PrintTypes(void) {
  int i;
  printf("The partition type may also be given as one of these aliases:\n\n");
  for (i = 0; i < ARRAY_COUNT(supported_types); ++i) {
    printf("    %-10s  %s\n", supported_types[i].name,
                          supported_types[i].description);
  }
  printf("\n");
}

GptHeader* GetGptHeader(const GptData *gpt) {
  if (gpt->valid_headers & MASK_PRIMARY)
    return (GptHeader*)gpt->primary_header;
  else if (gpt->valid_headers & MASK_SECONDARY)
    return (GptHeader*)gpt->secondary_header;
  else
    return 0;
}

uint32_t GetNumberOfEntries(const struct drive *drive) {
  if (drive->is_mtd)
    return MTD_MAX_PARTITIONS;

  GptHeader *header = GetGptHeader(&drive->gpt);
  if (!header)
    return 0;
  return header->number_of_entries;
}

GptEntry *GetEntry(GptData *gpt, int secondary, uint32_t entry_index) {
  GptHeader *header = GetGptHeader(gpt);
  uint8_t *entries;
  uint32_t stride = header->size_of_entry;
  require(stride);
  require(entry_index < header->number_of_entries);

  if (secondary == PRIMARY) {
    entries = gpt->primary_entries;
  } else if (secondary == SECONDARY) {
    entries = gpt->secondary_entries;
  } else {  /* ANY_VALID */
    require(secondary == ANY_VALID);
    if (gpt->valid_entries & MASK_PRIMARY) {
      entries = gpt->primary_entries;
    } else {
      require(gpt->valid_entries & MASK_SECONDARY);
      entries = gpt->secondary_entries;
    }
  }

  return (GptEntry*)(&entries[stride * entry_index]);
}

MtdDiskPartition* MtdGetEntry(MtdData *mtd, int secondary, uint32_t index) {
  if (index >= MTD_MAX_PARTITIONS)
    return NULL;
  return &mtd->primary.partitions[index];
}

void SetPriority(struct drive *drive, int secondary, uint32_t entry_index,
                 int priority) {
  require(priority >= 0 && priority <= CGPT_ATTRIBUTE_MAX_PRIORITY);
  if (drive->is_mtd) {
    MtdDiskPartition *e = MtdGetEntry(&drive->mtd, secondary, entry_index);
    MtdSetEntryPriority(e, priority);
  } else {
    GptEntry *entry;
    entry = GetEntry(&drive->gpt, secondary, entry_index);
    SetEntryPriority(entry, priority);
  }
}

int GetPriority(struct drive *drive, int secondary, uint32_t entry_index) {
  if (drive->is_mtd) {
    MtdDiskPartition *e = MtdGetEntry(&drive->mtd, secondary, entry_index);
    return MtdGetEntryPriority(e);
  } else {
    GptEntry *entry;
    entry = GetEntry(&drive->gpt, secondary, entry_index);
    return GetEntryPriority(entry);
  }
}

void SetTries(struct drive *drive, int secondary, uint32_t entry_index,
              int tries) {
  require(tries >= 0 && tries <= CGPT_ATTRIBUTE_MAX_TRIES);
  if (drive->is_mtd) {
    MtdDiskPartition *e = MtdGetEntry(&drive->mtd, secondary, entry_index);
    MtdSetEntryTries(e, tries);
  } else {
    GptEntry *entry;
    entry = GetEntry(&drive->gpt, secondary, entry_index);
    SetEntryTries(entry, tries);
  }
}

int GetTries(struct drive *drive, int secondary, uint32_t entry_index) {
  if (drive->is_mtd) {
    MtdDiskPartition *e = MtdGetEntry(&drive->mtd, secondary, entry_index);
    return MtdGetEntryTries(e);
  } else {
    GptEntry *entry;
    entry = GetEntry(&drive->gpt, secondary, entry_index);
    return GetEntryTries(entry);
  }
}

void SetSuccessful(struct drive *drive, int secondary, uint32_t entry_index,
                   int success) {
  require(success >= 0 && success <= CGPT_ATTRIBUTE_MAX_SUCCESSFUL);
  if (drive->is_mtd) {
    MtdDiskPartition *e = MtdGetEntry(&drive->mtd, secondary, entry_index);
    MtdSetEntrySuccessful(e, success);
  } else {
    GptEntry *entry;
    entry = GetEntry(&drive->gpt, secondary, entry_index);
    SetEntrySuccessful(entry, success);
  }
}

int GetSuccessful(struct drive *drive, int secondary, uint32_t entry_index) {
  if (drive->is_mtd) {
    MtdDiskPartition *e = MtdGetEntry(&drive->mtd, secondary, entry_index);
    return MtdGetEntrySuccessful(e);
  } else {
    GptEntry *entry;
    entry = GetEntry(&drive->gpt, secondary, entry_index);
    return GetEntrySuccessful(entry);
  }
}

void SetRaw(struct drive *drive, int secondary, uint32_t entry_index,
            uint32_t raw) {
  if (drive->is_mtd) {
    MtdDiskPartition *e = MtdGetEntry(&drive->mtd, secondary, entry_index);
    e->flags = raw;
  } else {
    GptEntry *entry;
    entry = GetEntry(&drive->gpt, secondary, entry_index);
    entry->attrs.fields.gpt_att = (uint16_t)raw;
  }
}

void UpdateAllEntries(struct drive *drive) {
  if (drive->is_mtd) {
    drive->mtd.modified = 1;
    drive->mtd.primary.crc32 = MtdHeaderCrc(&drive->mtd.primary);
  } else {
    RepairEntries(&drive->gpt, MASK_PRIMARY);
    RepairHeader(&drive->gpt, MASK_PRIMARY);

    drive->gpt.modified |= (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1 |
                           GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2);
    UpdateCrc(&drive->gpt);
  }
}

int IsUnused(struct drive *drive, int secondary, uint32_t index) {
  if (drive->is_mtd) {
    MtdDiskPartition *e = MtdGetEntry(&drive->mtd, secondary, index);
    return MtdGetEntryType(e) == MTD_PARTITION_TYPE_UNUSED;
  } else {
    GptEntry *entry;
    entry = GetEntry(&drive->gpt, secondary, index);
    return GuidIsZero(&entry->type);
  }
}

int IsKernel(struct drive *drive, int secondary, uint32_t index) {
  if (drive->is_mtd) {
    MtdDiskPartition *e = MtdGetEntry(&drive->mtd, secondary, index);
    return MtdGetEntryType(e) == MTD_PARTITION_TYPE_CHROMEOS_KERNEL;
  } else {
    GptEntry *entry;
    entry = GetEntry(&drive->gpt, secondary, index);
    return GuidEqual(&entry->type, &guid_chromeos_kernel);
  }
}

#define TOSTRING(A) #A
const char *GptError(int errnum) {
  const char *error_string[] = {
    TOSTRING(GPT_SUCCESS),
    TOSTRING(GPT_ERROR_NO_VALID_KERNEL),
    TOSTRING(GPT_ERROR_INVALID_HEADERS),
    TOSTRING(GPT_ERROR_INVALID_ENTRIES),
    TOSTRING(GPT_ERROR_INVALID_SECTOR_SIZE),
    TOSTRING(GPT_ERROR_INVALID_SECTOR_NUMBER),
    TOSTRING(GPT_ERROR_INVALID_UPDATE_TYPE)
  };
  if (errnum < 0 || errnum >= ARRAY_COUNT(error_string))
    return "<illegal value>";
  return error_string[errnum];
}

/*  Update CRC value if necessary.  */
void UpdateCrc(GptData *gpt) {
  GptHeader *primary_header, *secondary_header;

  primary_header = (GptHeader*)gpt->primary_header;
  secondary_header = (GptHeader*)gpt->secondary_header;

  if (gpt->modified & GPT_MODIFIED_ENTRIES1 &&
      memcmp(primary_header, GPT_HEADER_SIGNATURE2,
             GPT_HEADER_SIGNATURE_SIZE)) {
    primary_header->entries_crc32 =
        Crc32(gpt->primary_entries, TOTAL_ENTRIES_SIZE);
  }
  if (gpt->modified & GPT_MODIFIED_ENTRIES2) {
    secondary_header->entries_crc32 =
        Crc32(gpt->secondary_entries, TOTAL_ENTRIES_SIZE);
  }
  if (gpt->modified & GPT_MODIFIED_HEADER1) {
    primary_header->header_crc32 = 0;
    primary_header->header_crc32 = Crc32(
        (const uint8_t *)primary_header, sizeof(GptHeader));
  }
  if (gpt->modified & GPT_MODIFIED_HEADER2) {
    secondary_header->header_crc32 = 0;
    secondary_header->header_crc32 = Crc32(
        (const uint8_t *)secondary_header, sizeof(GptHeader));
  }
}
/* Two headers are NOT bitwise identical. For example, my_lba pointers to header
 * itself so that my_lba in primary and secondary is definitely different.
 * Only the following fields should be identical.
 *
 *   first_usable_lba
 *   last_usable_lba
 *   number_of_entries
 *   size_of_entry
 *   disk_uuid
 *
 * If any of above field are not matched, overwrite secondary with primary since
 * we always trust primary.
 * If any one of header is invalid, copy from another. */
int IsSynonymous(const GptHeader* a, const GptHeader* b) {
  if ((a->first_usable_lba == b->first_usable_lba) &&
      (a->last_usable_lba == b->last_usable_lba) &&
      (a->number_of_entries == b->number_of_entries) &&
      (a->size_of_entry == b->size_of_entry) &&
      (!memcmp(&a->disk_uuid, &b->disk_uuid, sizeof(Guid))))
    return 1;
  return 0;
}

/* Primary entries and secondary entries should be bitwise identical.
 * If two entries tables are valid, compare them. If not the same,
 * overwrites secondary with primary (primary always has higher priority),
 * and marks secondary as modified.
 * If only one is valid, overwrites invalid one.
 * If all are invalid, does nothing.
 * This function returns bit masks for GptData.modified field.
 * Note that CRC is NOT re-computed in this function.
 */
uint8_t RepairEntries(GptData *gpt, const uint32_t valid_entries) {
  /* If we have an alternate GPT header signature, don't overwrite
   * the secondary GPT with the primary one as that might wipe the
   * partition table. Also don't overwrite the primary one with the
   * secondary one as that will stop Windows from booting. */
  GptHeader* h = (GptHeader*)(gpt->primary_header);
  if (!memcmp(h->signature, GPT_HEADER_SIGNATURE2, GPT_HEADER_SIGNATURE_SIZE))
    return 0;

  if (valid_entries == MASK_BOTH) {
    if (memcmp(gpt->primary_entries, gpt->secondary_entries,
               TOTAL_ENTRIES_SIZE)) {
      memcpy(gpt->secondary_entries, gpt->primary_entries, TOTAL_ENTRIES_SIZE);
      return GPT_MODIFIED_ENTRIES2;
    }
  } else if (valid_entries == MASK_PRIMARY) {
    memcpy(gpt->secondary_entries, gpt->primary_entries, TOTAL_ENTRIES_SIZE);
    return GPT_MODIFIED_ENTRIES2;
  } else if (valid_entries == MASK_SECONDARY) {
    memcpy(gpt->primary_entries, gpt->secondary_entries, TOTAL_ENTRIES_SIZE);
    return GPT_MODIFIED_ENTRIES1;
  }

  return 0;
}

/* The above five fields are shared between primary and secondary headers.
 * We can recover one header from another through copying those fields. */
void CopySynonymousParts(GptHeader* target, const GptHeader* source) {
  target->first_usable_lba = source->first_usable_lba;
  target->last_usable_lba = source->last_usable_lba;
  target->number_of_entries = source->number_of_entries;
  target->size_of_entry = source->size_of_entry;
  memcpy(&target->disk_uuid, &source->disk_uuid, sizeof(Guid));
}

/* This function repairs primary and secondary headers if possible.
 * If both headers are valid (CRC32 is correct) but
 *   a) indicate inconsistent usable LBA ranges,
 *   b) inconsistent partition entry size and number,
 *   c) inconsistent disk_uuid,
 * we will use the primary header to overwrite secondary header.
 * If primary is invalid (CRC32 is wrong), then we repair it from secondary.
 * If secondary is invalid (CRC32 is wrong), then we repair it from primary.
 * This function returns the bitmasks for modified header.
 * Note that CRC value is NOT re-computed in this function. UpdateCrc() will
 * do it later.
 */
uint8_t RepairHeader(GptData *gpt, const uint32_t valid_headers) {
  GptHeader *primary_header, *secondary_header;

  primary_header = (GptHeader*)gpt->primary_header;
  secondary_header = (GptHeader*)gpt->secondary_header;

  if (valid_headers == MASK_BOTH) {
    if (!IsSynonymous(primary_header, secondary_header)) {
      CopySynonymousParts(secondary_header, primary_header);
      return GPT_MODIFIED_HEADER2;
    }
  } else if (valid_headers == MASK_PRIMARY) {
    memcpy(secondary_header, primary_header, sizeof(GptHeader));
    secondary_header->my_lba = gpt->drive_sectors - 1;  /* the last sector */
    secondary_header->alternate_lba = primary_header->my_lba;
    secondary_header->entries_lba = secondary_header->my_lba -
        GPT_ENTRIES_SECTORS;
    return GPT_MODIFIED_HEADER2;
  } else if (valid_headers == MASK_SECONDARY) {
    memcpy(primary_header, secondary_header, sizeof(GptHeader));
    primary_header->my_lba = GPT_PMBR_SECTOR;  /* the second sector on drive */
    primary_header->alternate_lba = secondary_header->my_lba;
    primary_header->entries_lba = primary_header->my_lba + GPT_HEADER_SECTOR;
    return GPT_MODIFIED_HEADER1;
  }

  return 0;
}

int GuidEqual(const Guid *guid1, const Guid *guid2) {
  return (0 == memcmp(guid1, guid2, sizeof(Guid)));
}

int GuidIsZero(const Guid *gp) {
  return GuidEqual(gp, &guid_unused);
}

void PMBRToStr(struct pmbr *pmbr, char *str, unsigned int buflen) {
  char buf[GUID_STRLEN];
  if (GuidIsZero(&pmbr->boot_guid)) {
    require(snprintf(str, buflen, "PMBR") < buflen);
  } else {
    GuidToStr(&pmbr->boot_guid, buf, sizeof(buf));
    require(snprintf(str, buflen, "PMBR (Boot GUID: %s)", buf) < buflen);
  }
}


text:
fiddleFileDownload
======

### Title
Tree Panel AddTool Mixin

### Description
Fiddle exploring how to define a view Mixin class that is used by two different tree panels to add tool bar menu
button.

### Published Version Link
[https://fiddle.sencha.com/fiddle/ccp/preview](https://fiddle.sencha.com/fiddle/ccp/preview)

### Tags
ExtJS-5, accordion, tree, panel, addtool, mixin


text:
namespace UsageConsumerOverloads;

using System;
using System.IO;
using System.Threading.Tasks;
using UsageConsumer;
using UsageContracts;
using MassTransit;
using Microsoft.Extensions.Hosting;

public class Program
{
    public static async Task Main(string[] args)
    {
        await Host.CreateDefaultBuilder(args)
            .ConfigureServices(services =>
            {
                services.AddMassTransit(x =>
                {
                    x.UsingRabbitMq((cxt, cfg) =>
                    {
                        cfg.ReceiveEndpoint("order-service", e =>
                        {
                            // delegate consumer factory
                            e.Consumer(() => new SubmitOrderConsumer());

                            // another delegate consumer factory, with dependency
                            e.Consumer(() => new LogOrderSubmittedConsumer(Console.Out));

                            // a type-based factory that returns an object (specialized uses)
                            var consumerType = typeof(SubmitOrderConsumer);
                            e.Consumer(consumerType, type => Activator.CreateInstance(consumerType));
                        });
                    });
                });
            })
            .Build()
            .RunAsync();
    }
}

class LogOrderSubmittedConsumer :
    IConsumer<OrderSubmitted>
{
    readonly TextWriter _writer;

    public LogOrderSubmittedConsumer(TextWriter writer)
    {
        _writer = writer;
    }

    public async Task Consume(ConsumeContext<OrderSubmitted> context)
    {
        await _writer.WriteLineAsync($"Order submitted: {context.Message.OrderId}");
    }
}


text:
`Encore\Admin\Grid`类用于生成基于数据模型的表格,先来个例子,数据库中有`movies`表

```sql
CREATE TABLE `movies` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `director` int(10) unsigned NOT NULL,
  `describe` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `rate` tinyint unsigned NOT NULL,
  `released` enum(0, 1),
  `release_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  `created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  `updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

```

对应的数据模型为`App\Models\Movie`,下面的代码可以生成表`movies`的数据表格:

```php

use App\Models\Movie;
use Encore\Admin\Grid;
use Encore\Admin\Facades\Admin;

$grid = Admin::grid(Movie::class, function(Grid $grid){

    // 第一列显示id字段,并将这一列设置为可排序列
    $grid->id('ID')->sortable();

    // 第二列显示title字段,由于title字段名和Grid对象的title方法冲突,所以用Grid的column()方法代替
    $grid->column('title');

    // 第三列显示director字段,通过display($callback)方法设置这一列的显示内容为users表中对应的用户名
    $grid->director()->display(function($userId) {
        return User::find($userId)->name;
    });

    // 第四列显示为describe字段
    $grid->describe();

    // 第五列显示为rate字段
    $grid->rate();

    // 第六列显示released字段,通过display($callback)方法来格式化显示输出
    $grid->released('上映?')->display(function ($released) {
        return $released ? '是' : '否';
    });

    // 下面为三个时间字段的列显示
    $grid->release_at();
    $grid->created_at();
    $grid->updated_at();

    // filter($callback)方法用来设置表格的简单搜索框
    $grid->filter(function ($filter) {

        // 设置created_at字段的范围查询
        $filter->between('created_at', 'Created Time')->datetime();
    });
});

```

## 基本使用方法

#### 添加列
```php

// 直接通过字段名`username`添加列
$grid->username('用户名');

// 效果和上面一样
$grid->column('username', '用户名');

// 添加多列
$grid->columns('email', 'username' ...);
```

#### 修改来源数据
```php
$grid->model()->where('id', '>', 100);

$grid->model()->orderBy('id', 'desc');

$grid->model()->take(100);

...

```

其它查询方法可以参考`eloquent`的查询方法.

#### 设置每页显示行数

```php
// 默认为每页20条
$grid->paginate(15);
```

#### 修改显示输出

```php
use Illuminate\Support\Str;
$grid->text()->display(function($text) {
    return Str::limit($text, 30, '...');
});

$grid->name()->display(function ($name) {
    return "<span class='label'>$name</span>";
});

$grid->email()->display(function ($email) {
    return "mailto:$email";
});

// 添加不存在的字段
$grid->column('column_not_in_table')->display(function () {
    return 'blablabla....';
});
```

`display()`方法接收的匿名函数绑定了当前行的数据对象,可以在里面调用当前行的其它字段数据

```php
$grid->first_name();
$grid->last_name();

// 不存的字段列
$grid->column('full_name')->display(function () {
    return $this->first_name.' '.$this->last_name;
});
```

#### 禁用创建按钮
```php
$grid->disableCreateButton();
```

#### 禁用分页条
```php
$grid->disablePagination();
```

#### 禁用查询过滤器
```php
$grid->disableFilter();
```

#### 禁用导出数据按钮
```php
$grid->disableExport();
```

#### 禁用行选择checkbox
```php
$grid->disableRowSelector();
```

#### 禁用行操作列
```php
$grid->disableActions();
```

#### 设置分页选择器选项
```php
$grid->perPages([10, 20, 30, 40, 50]);
```

## 关联模型

### 一对一
`users`表和`profiles`表通过`profiles.user_id`字段生成一对一关联

```sql

CREATE TABLE `users` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`email` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

CREATE TABLE `profiles` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`age` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`gender` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
```

对应的数据模分别为:

```php

class User extends Model
{
    public function profile()
    {
        return $this->hasOne(Profile::class);
    }
}

class Profile extends Model
{
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

```

通过下面的代码可以关联在一个grid里面:

```php
Admin::grid(User::class, function (Grid $grid) {

    $grid->id('ID')->sortable();

    $grid->name();
    $grid->email();

    $grid->column('profile.age');
    $grid->column('profile.gender');

    //or
    $grid->profile()->age();
    $grid->profile()->gender();

    $grid->created_at();
    $grid->updated_at();
});

```

### 一对多

`posts`表和`comments`表通过`comments.post_id`字段生成一对多关联

```sql

CREATE TABLE `posts` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`content` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

CREATE TABLE `comments` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`post_id` int(10) unsigned NOT NULL,
`content` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
```

对应的数据模分别为:

```php

class Post extends Model
{
    public function comments()
    {
        return $this->hasMany(Comment::class);
    }
}

class Comment extends Model
{
    public function post()
    {
        return $this->belongsTo(Post::class);
    }
}

```

通过下面的代码可以让两个模型在grid里面互相关联:

```php

return Admin::grid(Post::class, function (Grid $grid) {
    $grid->id('id')->sortable();
    $grid->title();
    $grid->content();

    $grid->comments('评论数')->display(function ($comments) {
        $count = count($comments);
        return "<span class='label label-warning'>{$count}</span>";
    });

    $grid->created_at();
    $grid->updated_at();
});

return Admin::grid(Comment::class, function (Grid $grid) {
    $grid->id('id');
    $grid->post()->title();
    $grid->content();

    $grid->created_at()->sortable();
    $grid->updated_at();
});

```

### 多对多

`users`和`roles`表通过中间表`role_users`产生多对多关系

```sql

CREATE TABLE `users` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `username` varchar(190) COLLATE utf8_unicode_ci NOT NULL,
  `password` varchar(60) COLLATE utf8_unicode_ci NOT NULL,
  `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `users_username_unique` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

CREATE TABLE `roles` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
  `slug` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `roles_name_unique` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

CREATE TABLE `role_users` (
  `role_id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  KEY `role_users_role_id_user_id_index` (`role_id`,`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
```

对应的数据模分别为:

```php

class User extends Model
{
    public function roles()
    {
        return $this->belongsToMany(Role::class);
    }
}

class Role extends Model
{
    public function users()
    {
        return $this->belongsToMany(User::class);
    }
}

```

通过下面的代码可以让两个模型在grid里面互相关联:

```php
return Admin::grid(User::class, function (Grid $grid) {
    $grid->id('ID')->sortable();
    $grid->username();
    $grid->name();

    $grid->roles()->display(function ($roles) {

        $roles = array_map(function ($role) {
            return "<span class='label label-success'>{$role['name']}</span>";
        }, $roles);

        return join(' ', $roles);
    });

    $grid->created_at();
    $grid->updated_at();
});

```


text:
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
using Remotus.API.Net.Hubs;

namespace Remotus.Plugins.Spotify
{
    [Obsolete]
    public class SpotifyHub : HubBase
    {
        public SpotifyHub()
        {

        }

        public override string HubName => MethodBase.GetCurrentMethod().DeclaringType.Name;

        public override async Task OnConnected()
        {
            await base.OnConnected();
        }

    }
}


text:
using System.Threading.Tasks;
using Xamarin.Forms;

#pragma warning disable 4014

namespace Behaviors
{
	[Preserve(AllMembers = true)]
	public class RotateAction : AnimationBase, IAction
	{
		public static readonly BindableProperty FinalAngleProperty = BindableProperty.Create(nameof(FinalAngle), typeof(double), typeof(RotateAction), 0.0);
		public static readonly BindableProperty IsRelativeProperty = BindableProperty.Create(nameof(IsRelative), typeof(bool), typeof(RotateAction), false);
		public static readonly BindableProperty AxisProperty = BindableProperty.Create(nameof(Axis), typeof(RotationAxis), typeof(RotateAction), RotationAxis.Z);

		public double FinalAngle
		{
			get { return (double)GetValue(FinalAngleProperty); }
			set { SetValue(FinalAngleProperty, value); }
		}

		public bool IsRelative
		{
			get { return (bool)GetValue(IsRelativeProperty); }
			set { SetValue(IsRelativeProperty, value); }
		}

		public RotationAxis Axis
		{
			get { return (RotationAxis)GetValue(AxisProperty); }
			set { SetValue(AxisProperty, value); }
		}

		public async Task<bool> Execute(object sender, object parameter)
		{
			VisualElement element;
			if (TargetObject != null)
			{
				element = TargetObject as VisualElement;
			}
			else {
				element = sender as VisualElement;
			}

			if (element == null)
			{
				return false;
			}

			switch (Axis)
			{
				case RotationAxis.X:
					if (Await)
					{
						await element.RotateXTo(FinalAngle, (uint)Duration, GetEasingFunction());
					}
					else {
						element.RotateXTo(FinalAngle, (uint)Duration, GetEasingFunction());
					}
					break;
				case RotationAxis.Y:
					if (Await)
					{
						await element.RotateYTo(FinalAngle, (uint)Duration, GetEasingFunction());
					}
					else {
						element.RotateYTo(FinalAngle, (uint)Duration, GetEasingFunction());
					}
					break;
				case RotationAxis.Z:
					if (IsRelative)
					{
						if (Await)
						{
							await element.RelRotateTo(FinalAngle, (uint)Duration, GetEasingFunction());
						}
						else {
							element.RelRotateTo(FinalAngle, (uint)Duration, GetEasingFunction());
						}
					}
					else {
						if (Await)
						{
							await element.RotateTo(FinalAngle, (uint)Duration, GetEasingFunction());
						}
						else {
							element.RotateTo(FinalAngle, (uint)Duration, GetEasingFunction());
						}
					}
					break;
				default:
					break;
			}

			return true;
		}
	}
}